From 2d9d66932aef4623f977d462d41ae51ee4cd490e Mon Sep 17 00:00:00 2001 From: Phil Pirozhkov Date: Sun, 25 Jul 2021 17:43:39 +0300 Subject: [PATCH] Fix `inherit_mode` for arrays in extension default See https://github.com/rubocop/rubocop-rspec/issues/1126 `config/default.yml`: ```yaml RSpec: Language: Examples: inherit_mode: merge: - Regular Regular: - it - specify - example - scenario - its ``` `.rubocop.yml`: ```yaml require: - rubocop-rspec RSpec: Language: Examples: Regular: - mycustomexamplealias ``` Previously, locally-set `inherit_mode` in extension default configuration was not respected. --- changelog/fix_inherit_mode-for-extensions.md | 1 + lib/rubocop/config_loader_resolver.rb | 27 +++++++--- spec/rubocop/config_loader_spec.rb | 53 ++++++++++++++++++++ 3 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 changelog/fix_inherit_mode-for-extensions.md diff --git a/changelog/fix_inherit_mode-for-extensions.md b/changelog/fix_inherit_mode-for-extensions.md new file mode 100644 index 00000000000..8b135714dab --- /dev/null +++ b/changelog/fix_inherit_mode-for-extensions.md @@ -0,0 +1 @@ +* [#9952](https://github.com/rubocop/rubocop/pull/9952) [rubocop-rspec#1126](https://github.com/rubocop/rubocop-rspec/issues/1126): Fix `inherit_mode` for deeply nested configuration defined in extensions' default configuration. ([@pirj][]) diff --git a/lib/rubocop/config_loader_resolver.rb b/lib/rubocop/config_loader_resolver.rb index 324dc21ea52..e594691c6bc 100644 --- a/lib/rubocop/config_loader_resolver.rb +++ b/lib/rubocop/config_loader_resolver.rb @@ -108,7 +108,7 @@ def merge(base_hash, derived_hash, **opts) result.delete(key) elsif merge_hashes?(base_hash, derived_hash, key) result[key] = merge(base_hash[key], derived_hash[key], **opts) - elsif should_union?(base_hash, key, opts[:inherit_mode]) + elsif should_union?(derived_hash, base_hash, opts[:inherit_mode], key) result[key] = base_hash[key] | derived_hash[key] elsif opts[:debug] warn_on_duplicate_setting(base_hash, derived_hash, key, **opts) @@ -183,11 +183,26 @@ def determine_inherit_mode(hash, key) local_inherit || hash['inherit_mode'] || {} end - def should_union?(base_hash, key, inherit_mode) - base_hash[key].is_a?(Array) && - inherit_mode && - inherit_mode['merge'] && - inherit_mode['merge'].include?(key) + def should_union?(derived_hash, base_hash, root_mode, key) + return false unless base_hash[key].is_a?(Array) + + derived_mode = derived_hash['inherit_mode'] + return false if should_override?(derived_mode, key) + return true if should_merge?(derived_mode, key) + + base_mode = base_hash['inherit_mode'] + return false if should_override?(base_mode, key) + return true if should_merge?(base_mode, key) + + should_merge?(root_mode, key) + end + + def should_merge?(mode, key) + mode && mode['merge'] && mode['merge'].include?(key) + end + + def should_override?(mode, key) + mode && mode['override'] && mode['override'].include?(key) end def merge_hashes?(base_hash, derived_hash, key) diff --git a/spec/rubocop/config_loader_spec.rb b/spec/rubocop/config_loader_spec.rb index 9f363d134f7..7f0767d19fe 100644 --- a/spec/rubocop/config_loader_spec.rb +++ b/spec/rubocop/config_loader_spec.rb @@ -537,6 +537,59 @@ end end + context 'when inherit_mode is specified for a nested element even ' \ + 'without explicitly specifying `inherit_from`/`inherit_gem`' do + let(:file_path) { '.rubocop.yml' } + + before do + stub_const('RuboCop::ConfigLoader::RUBOCOP_HOME', 'rubocop') + stub_const('RuboCop::ConfigLoader::DEFAULT_FILE', + File.join('rubocop', 'config', 'default.yml')) + + create_file('rubocop/config/default.yml', <<~YAML) + Language: + Examples: + inherit_mode: + merge: + - Regular + - Focused + Regular: + - it + Focused: + - fit + Skipped: + - xit + Pending: + - pending + YAML + create_file(file_path, <<~YAML) + Language: + Examples: + inherit_mode: + merge: + - Skipped + override: + - Focused + Regular: + - scenario + Focused: + - fscenario + Skipped: + - xscenario + Pending: + - later + YAML + end + + it 'respects the priority of `inherit_mode`, user-defined first' do + examples_configuration = configuration_from_file['Language']['Examples'] + expect(examples_configuration['Regular']).to contain_exactly('it', 'scenario') + expect(examples_configuration['Focused']).to contain_exactly('fscenario') + expect(examples_configuration['Skipped']).to contain_exactly('xit', 'xscenario') + expect(examples_configuration['Pending']).to contain_exactly('later') + end + end + context 'when a department is disabled', :restore_registry do let(:file_path) { '.rubocop.yml' }