Repository navigation
[Fix #15042] Add DisallowedCops option to Style/DisableCopsWithinSourceCodeDirective - #15050
Conversation
588a5ad to
d19e9bf
Compare
|
@bbatsov Please review when you are available. Some people need this one. |
|
This pull request has been automatically marked as stale because it has not had any recent activity. It will be closed soon if no further activity occurs. Thank you for your contribution and understanding! |
|
still waiting on its merge |
|
I haven't forgotten about this and I'll take a closer look soon. |
|
Thank you so much! |
There was a problem hiding this comment.
Suggest file rename to something like:changelog/new_add_disallowed_cops_option_20260323210952.md
There was a problem hiding this comment.
Done — renamed to changelog/new_add_disallowed_cops_option_20260323210952.md.
| # | ||
| # Alternatively, specific cops can be disallowed with the `DisallowedCops` | ||
| # configuration. When `DisallowedCops` is set, only directives for the listed | ||
| # cops (and `rubocop:disable all`) will be flagged. This is useful when you want |
There was a problem hiding this comment.
:enable all is also flagged, so let's shorten this accordiingly
| # cops (and `rubocop:disable all`) will be flagged. This is useful when you want | |
| # cops (and `all`) will be flagged. This is useful when you want |
| end | ||
| end | ||
|
|
||
| context 'when enabling all cops' do |
There was a problem hiding this comment.
I suggest the corresponding disable spec as well, something like
context 'when disabling all cops' do
it 'registers an offense and corrects' do
expect_offense(<<~RUBY)
foo # rubocop:disable all
^^^^^^^^^^^^^^^^^^^^^ RuboCop disable/enable directives for `all` are not permitted.
RUBY
expect_correction(<<~RUBY)
foo#{trailing_whitespace}
RUBY
end
endThere was a problem hiding this comment.
Good catch — but the suggested spec actually fails: # rubocop:disable all disables Style/DisableCopsWithinSourceCodeDirective itself on that line, so the offense is suppressed by Cop::Base#enabled_line? before it is registered. This is pre-existing behavior on master (it also applies without DisallowedCops/AllowedCops — easy to verify with --force-default-config on a file containing foo # rubocop:disable all), and it is why the existing "disabling all cops" spec at the top of this file uses rubocop:enable all rather than disable all.
Making disable all flaggable would require bypassing directive suppression for this cop, which feels out of scope for this PR. I added a spec documenting the current behavior instead, with a comment explaining the self-suppression.
There was a problem hiding this comment.
Makes a ton of sense, and there's a separate issue about addressing that. Thanks for explaining!
| end | ||
| else | ||
| directive_cops - allowed_cops | ||
| end |
There was a problem hiding this comment.
This logic is perfectly clear, but in the interests of making future development easier, we can add a test for it like so:
context 'when both AllowedCops and DisallowedCops are set' do
let(:cop_config) do
{ 'AllowedCops' => ['Lint/Void'], 'DisallowedCops' => ['Security/Eval'] }
end
it 'gives DisallowedCops precedence and ignores AllowedCops' do
expect_offense(<<~RUBY)
foo # rubocop:disable Lint/Void, Security/Eval
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RuboCop disable/enable directives for `Security/Eval` are not permitted.
RUBY
expect_correction(<<~RUBY)
foo # rubocop:disable Lint/Void
RUBY
end
endThere was a problem hiding this comment.
Done — added the precedence spec (with the offense carets adjusted to match the actual range length).
|
@hammadxcm thanks so much for submitting this. I added some very minor comments in hope that they speed along acceptance. All non-blocking nits from my perspective. |
…WithinSourceCodeDirective`
d19e9bf to
e5c4ee6
Compare
Its done. @corsonknowles Please review. |
All looks good to me, and thanks for the explanation on the disable all part. I hope we're in good shape for when @bbatsov gets to take a deeper look |
|
Just dropping by to say I continue to be excited about this. |
|
In order to help validate this for general adoption, we've ported this PR into our Rails monorepo as a local cop to exercise Here are the useful findings:
Across all 6,794 files containing a rubocop:disable|enable|todo directive (~14,000 directive comments), the offense set is identical before and after the switch. Handling of Repeating that he directive_cops.include?('all') guard is a nice catch. It keeps For what it's worth, the reason we want this option is exactly the one in your description: an allowlist has to be revisited on every RuboCop or extension upgrade, because newly added cops land outside it and start failing builds. A denylist of genuinely critical cops or already perfectly observed rules makes upgrades a non-event for the config for this Cop, which is very nice for keeping up to date. The very tiniest of nits: We had one good finding. In summary, "works great, validated at scale" |

Fixes #15042
Summary
DisallowedCopsconfiguration option toStyle/DisableCopsWithinSourceCodeDirective, the inverse of the existingAllowedCops.DisallowedCopsis set, only directives for the listed cops (andrubocop:disable all) are flagged — all other cops can be freely disabled.Lint/Void) from being disabled rather than allowlisting hundreds of cops.Context
This addresses a different use case from #14598. Where #14598 is about preventing
Style/DisableCopsWithinSourceCodeDirectiveitself from being disabled, this feature is about selectively protecting a small set of critical cops without needing to allowlist every other cop. See the discussion in #14598 and the prior PR #14705 for background.Example configuration
With this config,
# rubocop:disable Lint/Voidis flagged, but# rubocop:disable Metrics/AbcSizeis allowed.Test plan
DisallowedCops: single disallowed cop, non-disallowed cop, mixed directives, multiple disallowed cops,rubocop:enable all, and leading source commentsAllowedCopsspecs still pass (no regressions)bundle exec rakepasses (all specs + self-lint + doc syntax check)Before submitting the PR make sure the following are checked:
[Fix #issue-number](if the related issue exists).master(if not - rebase it).bundle exec rake default. It executes all tests and runs RuboCop on its own code.{change_type}_{change_description}.mdif the new code introduces user-observable changes. See changelog entry format for details.