diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e5a668751a..d246c0b5008 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ * [#5319](https://github.com/bbatsov/rubocop/pull/5319): Add new `Security/Open` cop. ([@mame][]) * Add `EnforcedStyleForEmptyBrackets` configuration to `Layout/SpaceInsideReferenceBrackets`.([@garettarrowood][]) * [#5358](https://github.com/bbatsov/rubocop/pull/5358): `--no-auto-gen-timestamp` CLI option suppresses the inclusion of the date and time it was generated in auto-generated config. ([@dominicsayers][]) +* [#5388](https://github.com/bbatsov/rubocop/issues/5388): Add new `Style/UnlessMultipleConditions` cop. ([@wata727][]) ### Bug fixes diff --git a/config/enabled.yml b/config/enabled.yml index 70f90c8ce0b..200917eec54 100644 --- a/config/enabled.yml +++ b/config/enabled.yml @@ -1969,6 +1969,10 @@ Style/UnlessElse: StyleGuide: '#no-else-with-unless' Enabled: true +Style/UnlessMultipleConditions: + Description: 'Do not use `unless` with multiple conditions.' + Enabled: true + Style/UnneededCapitalW: Description: 'Checks for %W when interpolation is not needed.' Enabled: true diff --git a/lib/rubocop.rb b/lib/rubocop.rb index 6bc539fa6a4..580f1e56837 100644 --- a/lib/rubocop.rb +++ b/lib/rubocop.rb @@ -508,6 +508,7 @@ require_relative 'rubocop/cop/style/trailing_underscore_variable' require_relative 'rubocop/cop/style/trivial_accessors' require_relative 'rubocop/cop/style/unless_else' +require_relative 'rubocop/cop/style/unless_multiple_conditions' require_relative 'rubocop/cop/style/unneeded_capital_w' require_relative 'rubocop/cop/style/unneeded_interpolation' require_relative 'rubocop/cop/style/unneeded_percent_q' diff --git a/lib/rubocop/cop/style/unless_multiple_conditions.rb b/lib/rubocop/cop/style/unless_multiple_conditions.rb new file mode 100644 index 00000000000..b0113a2c274 --- /dev/null +++ b/lib/rubocop/cop/style/unless_multiple_conditions.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Style + # This cop checks that `unless` is not used with multiple conditions. + # In general, using multiple conditions with `unless` reduces readability. + # + # @example + # # bad + # unless foo && bar + # something + # end + # + # # bad + # unless foo || bar + # something + # end + # + # # good + # if !foo || !bar + # something + # end + # + # # good + # if !foo && !bar + # something + # end + class UnlessMultipleConditions < Cop + MSG = 'Avoid using `unless` with multiple conditions.'.freeze + + def on_if(node) + return unless node.unless? + + add_offense(node.condition) if node.condition.and_type? || + node.condition.or_type? + end + end + end + end +end diff --git a/manual/cops.md b/manual/cops.md index a77b8ecf781..e5b89fe40ab 100644 --- a/manual/cops.md +++ b/manual/cops.md @@ -511,6 +511,7 @@ In the following section you find all available cops: * [Style/TrailingUnderscoreVariable](cops_style.md#styletrailingunderscorevariable) * [Style/TrivialAccessors](cops_style.md#styletrivialaccessors) * [Style/UnlessElse](cops_style.md#styleunlesselse) +* [Style/UnlessMultipleConditions](cops_style.md#styleunlessmultipleconditions) * [Style/UnneededCapitalW](cops_style.md#styleunneededcapitalw) * [Style/UnneededInterpolation](cops_style.md#styleunneededinterpolation) * [Style/UnneededPercentQ](cops_style.md#styleunneededpercentq) diff --git a/manual/cops_style.md b/manual/cops_style.md index 25732ab6f78..83bc334e09f 100644 --- a/manual/cops_style.md +++ b/manual/cops_style.md @@ -5659,6 +5659,39 @@ end * [https://github.com/bbatsov/ruby-style-guide#no-else-with-unless](https://github.com/bbatsov/ruby-style-guide#no-else-with-unless) +## Style/UnlessMultipleConditions + +Enabled by default | Supports autocorrection +--- | --- +Enabled | No + +This cop checks that `unless` is not used with multiple conditions. +In general, using multiple conditions with `unless` reduces readability. + +### Examples + +```ruby +# bad +unless foo && bar + something +end + +# bad +unless foo || bar + something +end + +# good +if !foo || !bar + something +end + +# good +if !foo && !bar + something +end +``` + ## Style/UnneededCapitalW Enabled by default | Supports autocorrection diff --git a/spec/rubocop/cop/style/unless_multiple_conditions_spec.rb b/spec/rubocop/cop/style/unless_multiple_conditions_spec.rb new file mode 100644 index 00000000000..d156e9d167e --- /dev/null +++ b/spec/rubocop/cop/style/unless_multiple_conditions_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +RSpec.describe RuboCop::Cop::Style::UnlessMultipleConditions do + subject(:cop) { described_class.new(config) } + + let(:config) { RuboCop::Config.new } + + it 'registers an offense when using `unless`' \ + 'with multiple `and` conditions' do + expect_offense(<<-RUBY.strip_indent) + unless foo && bar + ^^^^^^^^^^ Avoid using `unless` with multiple conditions. + something + end + RUBY + end + + it 'registers an offense when using `unless` with multiple `or` conditions' do + expect_offense(<<-RUBY.strip_indent) + unless foo || bar + ^^^^^^^^^^ Avoid using `unless` with multiple conditions. + something + end + RUBY + end + + it 'does not register an offense when using `if`' \ + 'with multiple `and` conditions' do + expect_no_offenses(<<-RUBY.strip_indent) + if !foo && !bar + something + end + RUBY + end + + it 'does not register an offense when using `if`' \ + 'with multiple `or` conditions' do + expect_no_offenses(<<-RUBY.strip_indent) + if !foo || !bar + something + end + RUBY + end + + it 'does not register an offense when using `unless` with single condition' do + expect_no_offenses(<<-RUBY.strip_indent) + unless foo + something + end + RUBY + end +end