Rubocop: Layout/ClassStructure: auto-correction infinite loop

Created on 31 Aug 2020  路  7Comments  路  Source: rubocop-hq/rubocop

See https://github.com/rubocop-hq/rubocop/issues/8622#issuecomment-688517420

I did not investigate, but the following code generates an endless loop (even after #8620 was merged): https://gist.github.com/marcandre/52b4dad4b5cd01e0141662e03feee60b

bug help wanted

Most helpful comment

Dumb me, I did not run autocorrection, so how I supposed to get an infinite loop? 馃槃

Ok, reproduced, so will investigate.

All 7 comments

Can't reproduce using rubocop's master.

Odd. I just tried again with master and both rubocop-ast master and v0.3.0, on my two machines, and I still get an infinite loop error.

  1. Is that an infinite loop for Layout/EmptyLineBetweenDefs?
  2. Do you ran rubocop on that file only? Can you copy-paste the exact cli command?
1. Is that an infinite loop for `Layout/EmptyLineBetweenDefs`?

No idea, did not investigate. I opened a separate issue because we don't say which are the responsible cops

2. Do you ran rubocop on that file only? Can you copy-paste the exact cli command?

Yes.

Copy-paste into t.rb

$ bundle exec rubocop -A t.rb
Inspecting 1 file
W

Offenses:

t.rb:19:11: C: [Corrected] Layout/EmptyLinesAroundAccessModifier: Keep a blank line before and after private.
          private
          ^^^^^^^
t.rb:19:11: C: [Corrected] Layout/EmptyLinesAroundAccessModifier: Keep a blank line before and after protected.
          protected
          ^^^^^^^^^
t.rb:36:11: C: [Corrected] Layout/ClassStructure: initializer is supposed to appear before private_methods.
          def initialize(compiler, sequence:, var:) ...
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
t.rb:37:11: C: [Corrected] Layout/ClassStructure: initializer is supposed to appear before private_methods.
          def initialize(compiler, sequence:, var:) ...
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
t.rb:48:49: C: Layout/CommentIndentation: Incorrect indentation detected (column 48 instead of 14).
                                                # variadic nodes
                                                ^^^^^^^^^^^^^^^^
t.rb:59:11: C: [Corrected] Layout/ClassStructure: constants is supposed to appear before private_methods.
          DELTA = 1
          ^^^^^^^^^
t.rb:59:49: C: Layout/CommentIndentation: Incorrect indentation detected (column 48 instead of 14).
                                                #        is stored in DELTA
                                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^
t.rb:62:49: C: Layout/CommentIndentation: Incorrect indentation detected (column 48 instead of 14).
                                                # Must be true if `@cur_index` is `:variadic_mode`
                                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
t.rb:63:11: C: [Corrected] Layout/ClassStructure: initializer is supposed to appear before private_methods.
          def initialize(compiler, sequence:, var:) ...
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
t.rb:68:1: C: [Corrected] Layout/EmptyLines: Extra blank line detected.
t.rb:69:1: C: [Corrected] Layout/EmptyLines: Extra blank line detected.
t.rb:94:86: C: [Corrected] Style/IfUnlessModifier: Modifier form of if makes the line too long.
                all_matched_check = "&&\n#{matched}.size == #{node.term_nodes.size}" if node.rest_node
                                                                                     ^^
t.rb:95:1: C: [Corrected] Layout/EmptyLines: Extra blank line detected.
t.rb:95:11: W: [Corrected] Lint/UselessAccessModifier: Useless private access modifier.
          private
          ^^^^^^^
t.rb:149:43: C: [Corrected] Style/SymbolProc: Pass &:in_sync as an argument to all? instead of a block.
            @in_sync = sub_compilers.all? { |sub| sub.in_sync }
                                          ^^^^^^^^^^^^^^^^^^^^^
t.rb:263:11: C: [Corrected] Layout/ClassStructure: protected_methods is supposed to appear before private_methods.
          def compile_terms(children = @seq.children, last_arity = 0..0) ...
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
t.rb:363:11: C: [Corrected] Layout/ClassStructure: constants is supposed to appear before private_methods.
          DELTA = 1
          ^^^^^^^^^
t.rb:364:11: C: [Corrected] Layout/ClassStructure: constants is supposed to appear before private_methods.
          DELTA = 1
          ^^^^^^^^^

0 files inspected, 18 offenses detected, 15 offenses corrected, 3 offenses auto-correctable
Infinite loop detected in /Users/mal/rubocop/t.rb.
/Users/mal/rubocop/lib/rubocop/runner.rb:278:in `block in iterate_until_no_changes'
/Users/mal/rubocop/lib/rubocop/runner.rb:274:in `loop'
/Users/mal/rubocop/lib/rubocop/runner.rb:274:in `iterate_until_no_changes'
/Users/mal/rubocop/lib/rubocop/runner.rb:245:in `do_inspection_loop'
/Users/mal/rubocop/lib/rubocop/runner.rb:125:in `block in file_offenses'
/Users/mal/rubocop/lib/rubocop/runner.rb:150:in `file_offense_cache'
/Users/mal/rubocop/lib/rubocop/runner.rb:124:in `file_offenses'
/Users/mal/rubocop/lib/rubocop/runner.rb:115:in `process_file'
/Users/mal/rubocop/lib/rubocop/runner.rb:94:in `block in each_inspected_file'
/Users/mal/rubocop/lib/rubocop/runner.rb:93:in `each'
/Users/mal/rubocop/lib/rubocop/runner.rb:93:in `reduce'
/Users/mal/rubocop/lib/rubocop/runner.rb:93:in `each_inspected_file'
/Users/mal/rubocop/lib/rubocop/runner.rb:79:in `inspect_files'
/Users/mal/rubocop/lib/rubocop/runner.rb:40:in `run'
/Users/mal/rubocop/lib/rubocop/cli/command/execute_runner.rb:22:in `execute_runner'
/Users/mal/rubocop/lib/rubocop/cli/command/execute_runner.rb:14:in `run'
/Users/mal/rubocop/lib/rubocop/cli/command.rb:11:in `run'
/Users/mal/rubocop/lib/rubocop/cli/environment.rb:18:in `run'
/Users/mal/rubocop/lib/rubocop/cli.rb:65:in `run_command'
/Users/mal/rubocop/lib/rubocop/cli.rb:72:in `execute_runners'
/Users/mal/rubocop/lib/rubocop/cli.rb:41:in `run'
/Users/mal/rubocop/exe/rubocop:13:in `block in <top (required)>'
/Users/mal/.rvm/rubies/ruby-2.7.1/lib/ruby/2.7.0/benchmark.rb:308:in `realtime'
/Users/mal/rubocop/exe/rubocop:12:in `<top (required)>'
/Users/mal/.rvm/gems/ruby-2.7.1/bin/rubocop:23:in `load'
/Users/mal/.rvm/gems/ruby-2.7.1/bin/rubocop:23:in `<main>'
/Users/mal/.rvm/gems/ruby-2.7.1/bin/ruby_executable_hooks:24:in `eval'
/Users/mal/.rvm/gems/ruby-2.7.1/bin/ruby_executable_hooks:24:in `<main>'

Dumb me, I did not run autocorrection, so how I supposed to get an infinite loop? 馃槃

Ok, reproduced, so will investigate.

I investigated, so the minimal repro is

class SequenceSubcompiler
  def initialize; end

  private

  def handle_prev; end

  protected

  def sync; end
end

The culprit it the Layout/ClassStructure. Its autocorrection is not implemented correctly - this can be proved when to add autocorrection test for this https://github.com/rubocop-hq/rubocop/blob/0d5d52b19bc5f9b735c7132da761e7f83dc54fa5/spec/rubocop/cop/layout/class_structure_spec.rb#L121-L143
Haven't figured out how to deal with clobbering error, so no patch :(

Thanks for the investigation 馃憤

Was this page helpful?
0 / 5 - 0 ratings