Skip to content

Commit

Permalink
[Fix #11652] Generate inherit_from correctly also with ERB
Browse files Browse the repository at this point in the history
The problem with the existing algorithm, for generating the inherit_from line
in --auto-gen-config runs, was that we started by removing the inherit_from
(if present) and then inserted it later.

The things we must deal with in the algorithm are
* there could be a pre-existing inherit_from in .rubocop.yml, or not
* there could be a YAML doc start ---, or not
* there could be inheritance from one or multiple files

A more robust approach to the removing, generating, and re-inserting is to
assume that any pre-existing inherit_from directive is already in the right
place. By replacing it with a placeholder and later replacing the placeholder
with the new inheritance directive, we will get it right without much effort.
  • Loading branch information
jonas054 authored and bbatsov committed Oct 24, 2023
1 parent 41dda1f commit 21cad51
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 47 deletions.
1 change: 1 addition & 0 deletions changelog/fix_auto_gen_config_with_erb.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* [#11652](https://github.com/rubocop/rubocop/issues/11652): Make `--auto-gen-config` generate `inherit_from` correctly inside ERB `if`. ([@jonas054][])
15 changes: 10 additions & 5 deletions lib/rubocop/cli/command/auto_generate_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class AutoGenerateConfig < Base

AUTO_GENERATED_FILE = '.rubocop_todo.yml'
YAML_OPTIONAL_DOC_START = /\A---(\s+#|\s*\z)/.freeze
PLACEHOLDER = '###rubocop:inherit_here'

PHASE_1 = 'Phase 1 of 2: run Layout/LineLength cop'
PHASE_2 = 'Phase 2 of 2: run all cops'
Expand Down Expand Up @@ -125,15 +126,19 @@ def add_inheritance_from_auto_generated_file(config_file)

def existing_configuration(config_file)
File.read(config_file, encoding: Encoding::UTF_8)
.sub(/^inherit_from: *[^\n]+/, '')
.sub(/^inherit_from: *(\n *- *[^\n]+)+/, '')
.sub(/^inherit_from: *[^\n]+/, PLACEHOLDER)
.sub(/^inherit_from: *(\n *- *[^\n]+)+/, PLACEHOLDER)
end

def write_config_file(file_name, file_string, rubocop_yml_contents)
lines = /\S/.match?(rubocop_yml_contents) ? rubocop_yml_contents.split("\n", -1) : []
doc_start_index = lines.index { |line| YAML_OPTIONAL_DOC_START.match?(line) } || -1
lines.insert(doc_start_index + 1, "inherit_from:#{file_string}\n")
File.write(file_name, lines.join("\n"))
unless rubocop_yml_contents&.include?(PLACEHOLDER)
doc_start_index = lines.index { |line| YAML_OPTIONAL_DOC_START.match?(line) } || -1
lines.insert(doc_start_index + 1, PLACEHOLDER)
end
File.write(file_name, lines.join("\n")
.sub(/#{PLACEHOLDER}\n*/o, "inherit_from:#{file_string}\n\n")
.sub(/\n\n+\Z/, "\n"))
end

def relative_path_to_todo_from_options_config
Expand Down
111 changes: 69 additions & 42 deletions spec/rubocop/cli/auto_gen_config_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -300,50 +300,77 @@ def f
end
end

it 'overwrites an existing todo file' do
create_file('example1.rb', ['# frozen_string_literal: true',
'',
'x= 0 ',
'#' * 125,
'y ',
'puts x'])
create_file('.rubocop_todo.yml', <<~YAML)
Layout/LineLength:
Enabled: false
YAML
create_file('.rubocop.yml', ['inherit_from: .rubocop_todo.yml'])
expect(cli.run(['--auto-gen-config'])).to eq(0)
expect(File.readlines('.rubocop_todo.yml')[8..].map(&:chomp))
.to eq(['# Offense count: 1',
'# This cop supports safe autocorrection (--autocorrect).',
'# Configuration parameters: AllowForAlignment, ' \
'EnforcedStyleForExponentOperator.',
'# SupportedStylesForExponentOperator: space, no_space',
'Layout/SpaceAroundOperators:',
' Exclude:',
" - 'example1.rb'",
'',
'# Offense count: 2',
'# This cop supports safe autocorrection (--autocorrect).',
'# Configuration parameters: AllowInHeredoc.',
'Layout/TrailingWhitespace:',
' Exclude:',
" - 'example1.rb'",
'',
'# Offense count: 1',
'# This cop supports safe autocorrection (--autocorrect).',
'# Configuration parameters: AllowHeredoc, ' \
'AllowURI, URISchemes, IgnoreCopDirectives, ' \
'AllowedPatterns.',
'# URISchemes: http, https',
'Layout/LineLength:',
' Max: 125'])
shared_examples 'overwrites todo file' do |description, code|
context "when .rubocop.yml contains #{description}" do
it 'overwrites an existing todo file' do
create_file('example1.rb', ['# frozen_string_literal: true',
'',
'x= 0 ',
'#' * 125,
'y ',
'puts x'])
create_file('.rubocop_todo.yml', <<~YAML)
Layout/LineLength:
Enabled: false
YAML
create_empty_file('other.yml')
create_file('.rubocop.yml', code)
expect(cli.run(['--auto-gen-config'])).to eq(0)
expect(File.readlines('.rubocop_todo.yml')[8..].map(&:chomp))
.to eq(['# Offense count: 1',
'# This cop supports safe autocorrection (--autocorrect).',
'# Configuration parameters: AllowForAlignment, ' \
'EnforcedStyleForExponentOperator.',
'# SupportedStylesForExponentOperator: space, no_space',
'Layout/SpaceAroundOperators:',
' Exclude:',
" - 'example1.rb'",
'',
'# Offense count: 2',
'# This cop supports safe autocorrection (--autocorrect).',
'# Configuration parameters: AllowInHeredoc.',
'Layout/TrailingWhitespace:',
' Exclude:',
" - 'example1.rb'",
'',
'# Offense count: 1',
'# This cop supports safe autocorrection (--autocorrect).',
'# Configuration parameters: AllowHeredoc, ' \
'AllowURI, URISchemes, IgnoreCopDirectives, ' \
'AllowedPatterns.',
'# URISchemes: http, https',
'Layout/LineLength:',
' Max: 125'])
expect(File.readlines('.rubocop.yml').map(&:chomp)).to eq(code)

# Create new CLI instance to avoid using cached configuration.
new_cli = RuboCop::CLI.new

expect(new_cli.run(['example1.rb'])).to eq(0)
end
end
end

# Create new CLI instance to avoid using cached configuration.
new_cli = RuboCop::CLI.new
include_examples 'overwrites todo file',
'only the inherit_from line',
['inherit_from: .rubocop_todo.yml']

expect(new_cli.run(['example1.rb'])).to eq(0)
end
# Makes the shared example work the same with the conditional as without.
ENV['HZPKCEAXTFQLOWB'] = 'true'

include_examples 'overwrites todo file',
'a single line inherit_from in an ERB conditional',
['<% if ENV["HZPKCEAXTFQLOWB"] %>',
'inherit_from: .rubocop_todo.yml',
'<% end %>']

include_examples 'overwrites todo file',
'a multiline inherit_from in an ERB conditional',
['<% if ENV["HZPKCEAXTFQLOWB"] %>',
'inherit_from:',
' - .rubocop_todo.yml',
' - other.yml',
'<% end %>']

it 'honors rubocop:disable comments' do
create_file('example1.rb', ['#' * 121,
Expand Down

0 comments on commit 21cad51

Please sign in to comment.