diff --git a/rubocop/cop_todo.rb b/rubocop/cop_todo.rb index a36afc08673a3378a396e4cf9c2498bbcebce3c5..91f5e302d5700d954233e975045fa06e43d68b1f 100644 --- a/rubocop/cop_todo.rb +++ b/rubocop/cop_todo.rb @@ -26,6 +26,10 @@ def autocorrectable? @cop_class&.support_autocorrect? end + def generate? + previously_disabled || grace_period || files.any? + end + def to_yaml yaml = [] yaml << '---' @@ -39,8 +43,12 @@ def to_yaml end yaml << " #{RuboCop::Formatter::GracefulFormatter.grace_period_key_value}" if grace_period - yaml << ' Exclude:' - yaml.concat files.sort.map { |file| " - '#{file}'" } + + if files.any? + yaml << ' Exclude:' + yaml.concat files.sort.map { |file| " - '#{file}'" } + end + yaml << '' yaml.join("\n") diff --git a/rubocop/formatter/todo_formatter.rb b/rubocop/formatter/todo_formatter.rb index b1c6d1c1688b220e6a43688f80324d1982640e2d..5e49e2dc082411d3815bf6a74c1e096826f250fc 100644 --- a/rubocop/formatter/todo_formatter.rb +++ b/rubocop/formatter/todo_formatter.rb @@ -31,6 +31,7 @@ def initialize(output, _options = {}) @config_inspect_todo_dir = load_config_inspect_todo_dir @config_old_todo_yml = load_config_old_todo_yml check_multiple_configurations! + create_empty_todos(@config_inspect_todo_dir) super end @@ -47,11 +48,9 @@ def file_finished(file, offenses) def finished(_inspected_files) @todos.values.sort_by(&:cop_name).each do |todo| - todo.previously_disabled = previously_disabled?(todo) - todo.grace_period = grace_period?(todo) - validate_todo!(todo) - path = @todo_dir.write(todo.cop_name, todo.to_yaml) + next unless configure_and_validate_todo(todo) + path = @todo_dir.write(todo.cop_name, todo.to_yaml) output.puts "Written to #{relative_path(path)}\n" end end @@ -82,6 +81,14 @@ def check_multiple_configurations! raise "Multiple configurations found for cops:\n#{list}\n" end + # For each inspected cop TODO config create a TODO object to make sure + # the cop TODO config will be written even without any offenses. + def create_empty_todos(inspected_cop_config) + inspected_cop_config.each_key do |cop_name| + @todos[cop_name] + end + end + def config_for(todo) cop_name = todo.cop_name @@ -101,10 +108,15 @@ def grace_period?(todo) GracefulFormatter.grace_period?(todo.cop_name, config) end - def validate_todo!(todo) - return unless todo.previously_disabled && todo.grace_period + def configure_and_validate_todo(todo) + todo.previously_disabled = previously_disabled?(todo) + todo.grace_period = grace_period?(todo) + + if todo.previously_disabled && todo.grace_period + raise "#{todo.cop_name}: Cop must be enabled to use `#{GracefulFormatter.grace_period_key_value}`." + end - raise "#{todo.cop_name}: Cop must be enabled to use `#{GracefulFormatter.grace_period_key_value}`." + todo.generate? end def load_config_inspect_todo_dir diff --git a/spec/rubocop/cop_todo_spec.rb b/spec/rubocop/cop_todo_spec.rb index 3f9c378b3036e2f71c0c8feff604c50287f86a01..b02cd6ce5c8a57046609a1e71df57b41b2cb044b 100644 --- a/spec/rubocop/cop_todo_spec.rb +++ b/spec/rubocop/cop_todo_spec.rb @@ -66,6 +66,38 @@ end end + describe '#generate?' do + subject { cop_todo.generate? } + + context 'when empty todo' do + it { is_expected.to eq(false) } + end + + context 'when previously disabled' do + before do + cop_todo.previously_disabled = true + end + + it { is_expected.to eq(true) } + end + + context 'when in grace period' do + before do + cop_todo.grace_period = true + end + + it { is_expected.to eq(true) } + end + + context 'with offenses recorded' do + before do + cop_todo.record('a.rb', 1) + end + + it { is_expected.to eq(true) } + end + end + describe '#to_yaml' do subject(:yaml) { cop_todo.to_yaml } @@ -79,7 +111,6 @@ --- # Cop supports --auto-correct. #{cop_name}: - Exclude: YAML end end diff --git a/spec/rubocop/formatter/todo_formatter_spec.rb b/spec/rubocop/formatter/todo_formatter_spec.rb index edd846324096a6da1d5ded21b591756b7fdafc40..152987beefb1511bb4c1ed4edf9d7d688e6ea5eb 100644 --- a/spec/rubocop/formatter/todo_formatter_spec.rb +++ b/spec/rubocop/formatter/todo_formatter_spec.rb @@ -309,18 +309,78 @@ def run_formatter context 'without offenses detected' do before do + todo_dir.write('A/Cop', yaml) if yaml + todo_dir.inspect_all + formatter.started(%w[a.rb b.rb]) formatter.file_finished('a.rb', []) formatter.file_finished('b.rb', []) formatter.finished(%w[a.rb b.rb]) + + todo_dir.delete_inspected end - it 'does not output anything' do - expect(stdout.string).to eq('') + context 'without existing TODOs' do + let(:yaml) { nil } + + it 'does not output anything' do + expect(stdout.string).to eq('') + end + + it 'does not write any YAML files' do + expect(rubocop_todo_dir_listing).to be_empty + end end - it 'does not write any YAML files' do - expect(rubocop_todo_dir_listing).to be_empty + context 'with existing TODOs' do + context 'when existing offenses only' do + let(:yaml) do + <<~YAML + --- + A/Cop: + Exclude: + - x.rb + YAML + end + + it 'does not output anything' do + expect(stdout.string).to eq('') + end + + it 'does not write any YAML files' do + expect(rubocop_todo_dir_listing).to be_empty + end + end + + context 'when in grace period' do + let(:yaml) do + <<~YAML + --- + A/Cop: + Details: grace period + Exclude: + - x.rb + YAML + end + + it 'outputs its actions' do + expect(stdout.string).to eq(<<~OUTPUT) + Written to .rubocop_todo/a/cop.yml + OUTPUT + end + + it 'creates YAML file with Details only', :aggregate_failures do + expect(rubocop_todo_dir_listing).to contain_exactly( + 'a/cop.yml' + ) + + expect(todo_yml('A/Cop')).to eq(<<~YAML) + --- + A/Cop: + Details: grace period + YAML + end + end end end