From 74b422bd922b675a32e9d1cd736ca47494bd9be2 Mon Sep 17 00:00:00 2001 From: Brian Hawley Date: Mon, 24 Apr 2023 12:39:37 -0700 Subject: [PATCH] Update rubocop to 1.50.2 - General bundle update. - Update the docs too. - Add standard gem. Note this limits the rubocop-performance version. Fixes #351. --- Gemfile | 3 +- Gemfile.lock | 59 +++++++---- .../deprecated_attribute_assignment.md | 2 +- .../layout/first_array_element_line_break.md | 60 +++++------ .../layout/first_hash_element_line_break.md | 27 ++--- .../first_method_argument_line_break.md | 99 +++++++++---------- .../first_method_parameter_line_break.md | 99 ++++++++----------- .../layout/multiline_array_line_breaks.md | 34 ++----- .../layout/multiline_hash_key_line_breaks.md | 32 ++---- .../multiline_method_argument_line_breaks.md | 26 +---- .../multiline_method_parameter_line_breaks.md | 37 ++----- .../contents/lint/duplicate_match_pattern.md | 83 ++++++++++++++++ .../lint/redundant_string_coercion.md | 10 +- config/contents/lint/to_enum_arguments.md | 8 +- config/contents/naming/inclusive_language.md | 5 + .../style/class_and_module_children.md | 2 +- .../style/class_equality_comparison.md | 8 ++ config/contents/style/data_inheritance.md | 21 ++++ config/contents/style/file_empty.md | 6 +- config/contents/style/hash_syntax.md | 3 + config/contents/style/if_unless_modifier.md | 11 +++ .../method_call_with_args_parentheses.md | 79 ++++++++------- .../contents/style/redundant_fetch_block.md | 10 +- .../style/redundant_line_continuation.md | 60 +++++++++++ .../style/unless_logical_operators.md | 1 + 25 files changed, 441 insertions(+), 344 deletions(-) create mode 100644 config/contents/lint/duplicate_match_pattern.md create mode 100644 config/contents/style/data_inheritance.md create mode 100644 config/contents/style/redundant_line_continuation.md diff --git a/Gemfile b/Gemfile index bb45ba0c..79f0f97c 100644 --- a/Gemfile +++ b/Gemfile @@ -5,7 +5,7 @@ source 'https://rubygems.org' gem "activesupport", require: false gem "parser" gem "pry", require: false -gem "rubocop", "1.48.1", require: false +gem "rubocop", "1.50.2", require: false gem "rubocop-i18n", require: false gem "rubocop-graphql", require: false gem "rubocop-minitest", require: false @@ -17,6 +17,7 @@ gem "rubocop-sequel", require: false gem "rubocop-shopify", require: false gem "rubocop-sorbet", require: false gem "rubocop-thread_safety", require: false +gem "standard", ">= 1.0", require: false gem "test-prof", require: false group :test do diff --git a/Gemfile.lock b/Gemfile.lock index 0348ecd2..ba560207 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,13 +10,15 @@ GEM coderay (1.1.3) concurrent-ruby (1.2.2) diff-lcs (1.5.0) - i18n (1.12.0) + i18n (1.13.0) concurrent-ruby (~> 1.0) json (2.6.3) + language_server-protocol (3.17.0.3) + lint_roller (1.0.0) method_source (1.0.0) minitest (5.18.0) - parallel (1.22.1) - parser (3.2.1.1) + parallel (1.23.0) + parser (3.2.2.1) ast (~> 2.4.1) pry (0.14.2) coderay (~> 1.1) @@ -24,63 +26,77 @@ GEM rack (3.0.7) rainbow (3.1.1) rake (13.0.6) - regexp_parser (2.7.0) + regexp_parser (2.8.0) rexml (3.2.5) rspec (3.12.0) rspec-core (~> 3.12.0) rspec-expectations (~> 3.12.0) rspec-mocks (~> 3.12.0) - rspec-core (3.12.1) + rspec-core (3.12.2) rspec-support (~> 3.12.0) - rspec-expectations (3.12.2) + rspec-expectations (3.12.3) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.12.0) - rspec-mocks (3.12.4) + rspec-mocks (3.12.5) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.12.0) rspec-support (3.12.0) - rubocop (1.48.1) + rubocop (1.50.2) json (~> 2.3) parallel (~> 1.10) parser (>= 3.2.0.0) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.26.0, < 2.0) + rubocop-ast (>= 1.28.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.27.0) + rubocop-ast (1.28.1) parser (>= 3.2.1.0) - rubocop-capybara (2.17.1) + rubocop-capybara (2.18.0) rubocop (~> 1.41) - rubocop-graphql (1.0.1) + rubocop-factory_bot (2.22.0) + rubocop (~> 1.33) + rubocop-graphql (1.1.1) rubocop (>= 0.87, < 2) rubocop-i18n (3.0.0) rubocop (~> 1.0) - rubocop-minitest (0.29.0) + rubocop-minitest (0.31.0) rubocop (>= 1.39, < 2.0) rubocop-performance (1.16.0) rubocop (>= 1.7.0, < 2.0) rubocop-ast (>= 0.4.0) - rubocop-rails (2.18.0) + rubocop-rails (2.19.1) activesupport (>= 4.2.0) rack (>= 1.1) rubocop (>= 1.33.0, < 2.0) rubocop-rake (0.6.0) rubocop (~> 1.0) - rubocop-rspec (2.19.0) + rubocop-rspec (2.22.0) rubocop (~> 1.33) rubocop-capybara (~> 2.17) + rubocop-factory_bot (~> 2.22) rubocop-sequel (0.3.4) rubocop (~> 1.0) - rubocop-shopify (2.12.0) - rubocop (~> 1.44) + rubocop-shopify (2.13.0) + rubocop (~> 1.50) rubocop-sorbet (0.7.0) rubocop (>= 0.90.0) - rubocop-thread_safety (0.4.4) - rubocop (>= 0.53.0) + rubocop-thread_safety (0.5.1) + rubocop (>= 0.90.0) ruby-progressbar (1.13.0) - test-prof (1.2.0) + standard (1.28.2) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.0) + rubocop (~> 1.50.2) + standard-custom (~> 1.0.0) + standard-performance (~> 1.0.1) + standard-custom (1.0.0) + lint_roller (~> 1.0) + standard-performance (1.0.1) + lint_roller (~> 1.0) + rubocop-performance (~> 1.16.0) + test-prof (1.2.1) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.4.2) @@ -94,7 +110,7 @@ DEPENDENCIES pry rake rspec - rubocop (= 1.48.1) + rubocop (= 1.50.2) rubocop-graphql rubocop-i18n rubocop-minitest @@ -106,6 +122,7 @@ DEPENDENCIES rubocop-shopify rubocop-sorbet rubocop-thread_safety + standard (>= 1.0) test-prof BUNDLED WITH diff --git a/config/contents/gemspec/deprecated_attribute_assignment.md b/config/contents/gemspec/deprecated_attribute_assignment.md index a33e78bf..468094d1 100644 --- a/config/contents/gemspec/deprecated_attribute_assignment.md +++ b/config/contents/gemspec/deprecated_attribute_assignment.md @@ -1,4 +1,4 @@ -Checks that deprecated attribute attributes are not set in a gemspec file. +Checks that deprecated attributes are not set in a gemspec file. Removing deprecated attributes allows the user to receive smaller packed gems. ### Example: diff --git a/config/contents/layout/first_array_element_line_break.md b/config/contents/layout/first_array_element_line_break.md index 56feb353..638283f5 100644 --- a/config/contents/layout/first_array_element_line_break.md +++ b/config/contents/layout/first_array_element_line_break.md @@ -1,46 +1,36 @@ Checks for a line break before the first element in a multi-line array. -### Example: AllowMultilineFinalElement: false (default) - - # bad - [ :a, - :b] - - # bad - [ :a, { - :b => :c - }] +### Example: - # good - [:a, :b] + # bad + [ :a, + :b] - # good - [ - :a, - :b] + # good + [ + :a, + :b] - # good - [ - :a, { - :b => :c - }] + # good + [:a, :b] -### Example: AllowMultilineFinalElement: true +### Example: AllowMultilineFinalElement: false (default) - # bad - [ :a, - :b] + # bad + [ :a, { + :b => :c + }] - # good - [ :a, { - :b => :c - }] + # good + [ + :a, { + :b => :c + }] - # good - [ - :a, - :b] +### Example: AllowMultilineFinalElement: true - # good - [:a, :b] \ No newline at end of file + # good + [:a, { + :b => :c + }] diff --git a/config/contents/layout/first_hash_element_line_break.md b/config/contents/layout/first_hash_element_line_break.md index 89716aa0..baea2461 100644 --- a/config/contents/layout/first_hash_element_line_break.md +++ b/config/contents/layout/first_hash_element_line_break.md @@ -1,17 +1,12 @@ Checks for a line break before the first element in a multi-line hash. -### Example: AllowMultilineFinalElement: false (default) +### Example: # bad { a: 1, b: 2} - # bad - { a: 1, b: { - c: 3 - }} - # good { a: 1, @@ -23,11 +18,14 @@ multi-line hash. c: 3 }} -### Example: AllowMultilineFinalElement: true +### Example: AllowMultilineFinalElement: false (default) # bad - { a: 1, - b: 2} + { a: 1, b: { + c: 3 + }} + +### Example: AllowMultilineFinalElement: true # bad { a: 1, @@ -39,14 +37,3 @@ multi-line hash. { a: 1, b: { c: 3 }} - - # good - { - a: 1, - b: 2 } - - # good - { - a: 1, b: { - c: 3 - }} \ No newline at end of file diff --git a/config/contents/layout/first_method_argument_line_break.md b/config/contents/layout/first_method_argument_line_break.md index 4754283e..62a1936b 100644 --- a/config/contents/layout/first_method_argument_line_break.md +++ b/config/contents/layout/first_method_argument_line_break.md @@ -1,70 +1,59 @@ Checks for a line break before the first argument in a multi-line method call. -### Example: AllowMultilineFinalElement: false (default) +### Example: - # bad - method(foo, bar, - baz) + # bad + method(foo, bar, + baz) - # bad - method(foo, bar, { - baz: "a", - qux: "b", - }) - - # good - method( - foo, bar, - baz) - - # good - method( - foo, bar, { - baz: "a", - qux: "b", - }) + # good + method( + foo, bar, + baz) # ignored method foo, bar, baz -### Example: AllowMultilineFinalElement: true +### Example: AllowMultilineFinalElement: false (default) - # bad - method(foo, bar, - baz) + # bad + method(foo, bar, { + baz: "a", + qux: "b", + }) - # bad - method(foo, - bar, - { - baz: "a", - qux: "b", - } - ) + # good + method( + foo, bar, { + baz: "a", + qux: "b", + }) - # good - method(foo, bar, { +### Example: AllowMultilineFinalElement: true + + # bad + method(foo, + bar, + { baz: "a", qux: "b", - }) - - # good - method( - foo, bar, - baz) - - # good - method( - foo, - bar, - { - baz: "a", - qux: "b", - } - ) - - # ignored - method foo, bar, - baz \ No newline at end of file + } + ) + + # good + method(foo, bar, { + baz: "a", + qux: "b", + }) + + # good + method( + foo, + bar, + { + baz: "a", + qux: "b", + } + ) diff --git a/config/contents/layout/first_method_parameter_line_break.md b/config/contents/layout/first_method_parameter_line_break.md index 2dd6a3e5..dfa8f6aa 100644 --- a/config/contents/layout/first_method_parameter_line_break.md +++ b/config/contents/layout/first_method_parameter_line_break.md @@ -1,66 +1,49 @@ Checks for a line break before the first parameter in a multi-line method parameter definition. -### Example: AllowMultilineFinalElement: false (default) - - # bad - def method(foo, bar, - baz) - do_something - end - - # bad - def method(foo, bar, baz = { - :a => "b", - }) - do_something - end - - # good - def method( - foo, bar, - baz) - do_something - end +### Example: + + # bad + def method(foo, bar, + baz) + do_something + end + + # good + def method( + foo, bar, + baz) + do_something + end + + # ignored + def method foo, + bar + do_something + end - # good - def method( - foo, bar, baz = { - :a => "b", - }) - do_something - end +### Example: AllowMultilineFinalElement: false (default) - # ignored - def method foo, - bar - do_something - end + # bad + def method(foo, bar, baz = { + :a => "b", + }) + do_something + end + + # good + def method( + foo, bar, baz = { + :a => "b", + }) + do_something + end ### Example: AllowMultilineFinalElement: true - # bad - def method(foo, bar, - baz) - do_something - end - - # good - def method(foo, bar, baz = { - :a => "b", - }) - do_something - end - - # good - def method( - foo, bar, - baz) - do_something - end - - # ignored - def method foo, - bar - do_something - end + # good + def method(foo, bar, baz = { + :a => "b", + }) + do_something + end diff --git a/config/contents/layout/multiline_array_line_breaks.md b/config/contents/layout/multiline_array_line_breaks.md index b886331d..6d4720c4 100644 --- a/config/contents/layout/multiline_array_line_breaks.md +++ b/config/contents/layout/multiline_array_line_breaks.md @@ -1,7 +1,7 @@ Ensures that each item in a multi-line array starts on a separate line. -### Example: AllowMultilineFinalElement: false (default) +### Example: # bad [ @@ -9,11 +9,6 @@ starts on a separate line. c ] - # bad - [ a, b, foo( - bar - )] - # good [ a, @@ -30,31 +25,16 @@ starts on a separate line. ) ] -### Example: AllowMultilineFinalElement: true +### Example: AllowMultilineFinalElement: false (default) # bad - [ - a, b, - c - ] - - # good - [ a, b, foo( + [a, b, foo( bar )] - # good - [ - a, - b, - c - ] +### Example: AllowMultilineFinalElement: true # good - [ - a, - b, - foo( - bar - ) - ] \ No newline at end of file + [a, b, foo( + bar + )] diff --git a/config/contents/layout/multiline_hash_key_line_breaks.md b/config/contents/layout/multiline_hash_key_line_breaks.md index a9090e76..c52e2e9a 100644 --- a/config/contents/layout/multiline_hash_key_line_breaks.md +++ b/config/contents/layout/multiline_hash_key_line_breaks.md @@ -1,7 +1,7 @@ Ensures that each key in a multi-line hash starts on a separate line. -### Example: AllowMultilineFinalElement: false (default) +### Example: # bad { @@ -9,11 +9,6 @@ starts on a separate line. c: 3 } - # bad - { a: 1, b: { - c: 3, - }} - # good { a: 1, @@ -29,31 +24,16 @@ starts on a separate line. } } -### Example: AllowMultilineFinalElement: true +### Example: AllowMultilineFinalElement: false (default) # bad - { - a: 1, b: 2, - c: 3 - } - - # good { a: 1, b: { c: 3, }} - # good - { - a: 1, - b: 2, - c: 3 - } - +### Example: AllowMultilineFinalElement: true # good - { - a: 1, - b: { - c: 3, - } - } \ No newline at end of file + { a: 1, b: { + c: 3, + }} diff --git a/config/contents/layout/multiline_method_argument_line_breaks.md b/config/contents/layout/multiline_method_argument_line_breaks.md index 0928ae50..914c1fac 100644 --- a/config/contents/layout/multiline_method_argument_line_breaks.md +++ b/config/contents/layout/multiline_method_argument_line_breaks.md @@ -4,7 +4,7 @@ starts on a separate line. NOTE: This cop does not move the first argument, if you want that to be on a separate line, see `Layout/FirstMethodArgumentLineBreak`. -### Example: AllowMultilineFinalElement: false (default) +### Example: # bad foo(a, b, @@ -26,6 +26,8 @@ be on a separate line, see `Layout/FirstMethodArgumentLineBreak`. # good foo(a, b, c) +### Example: AllowMultilineFinalElement: false (default) + # good foo( a, @@ -37,26 +39,6 @@ be on a separate line, see `Layout/FirstMethodArgumentLineBreak`. ### Example: AllowMultilineFinalElement: true - # bad - foo(a, b, - c - ) - - # good - foo(a, b, { - foo: "bar", - }) - - # good - foo( - a, - b, - c - ) - - # good - foo(a, b, c) - # good foo( a, @@ -64,4 +46,4 @@ be on a separate line, see `Layout/FirstMethodArgumentLineBreak`. { foo: "bar", } - ) \ No newline at end of file + ) diff --git a/config/contents/layout/multiline_method_parameter_line_breaks.md b/config/contents/layout/multiline_method_parameter_line_breaks.md index 59fac371..e67defa6 100644 --- a/config/contents/layout/multiline_method_parameter_line_breaks.md +++ b/config/contents/layout/multiline_method_parameter_line_breaks.md @@ -4,7 +4,7 @@ starts on a separate line. NOTE: This cop does not move the first argument, if you want that to be on a separate line, see `Layout/FirstMethodParameterLineBreak`. -### Example: AllowMultilineFinalElement: false (default) +### Example: # bad def foo(a, b, @@ -12,12 +12,6 @@ be on a separate line, see `Layout/FirstMethodParameterLineBreak`. ) end - # bad - def foo(a, b = { - foo: "bar", - }) - end - # good def foo( a, @@ -39,37 +33,18 @@ be on a separate line, see `Layout/FirstMethodParameterLineBreak`. def foo(a, b, c) end -### Example: AllowMultilineFinalElement: true +### Example: AllowMultilineFinalElement: false (default) # bad - def foo(a, b, - c - ) - end - - # good def foo(a, b = { foo: "bar", }) end - # good - def foo( - a, - b, - c - ) - end +### Example: AllowMultilineFinalElement: true # good - def foo( - a, - b = { - foo: "bar", - } - ) + def foo(a, b = { + foo: "bar", + }) end - - # good - def foo(a, b, c) - end \ No newline at end of file diff --git a/config/contents/lint/duplicate_match_pattern.md b/config/contents/lint/duplicate_match_pattern.md new file mode 100644 index 00000000..981fd6af --- /dev/null +++ b/config/contents/lint/duplicate_match_pattern.md @@ -0,0 +1,83 @@ +Checks that there are no repeated patterns used in `in` keywords. + +### Example: + + # bad + case x + in 'first' + do_something + in 'first' + do_something_else + end + + # good + case x + in 'first' + do_something + in 'second' + do_something_else + end + + # bad - repeated alternate patterns with the same conditions don't depend on the order + case x + in foo | bar + first_method + in bar | foo + second_method + end + + # good + case x + in foo | bar + first_method + in bar | baz + second_method + end + + # bad - repeated hash patterns with the same conditions don't depend on the order + case x + in foo: a, bar: b + first_method + in bar: b, foo: a + second_method + end + + # good + case x + in foo: a, bar: b + first_method + in bar: b, baz: c + second_method + end + + # bad - repeated array patterns with elements in the same order + case x + in [foo, bar] + first_method + in [foo, bar] + second_method + end + + # good + case x + in [foo, bar] + first_method + in [bar, foo] + second_method + end + + # bad - repeated the same patterns and guard conditions + case x + in foo if bar + first_method + in foo if bar + second_method + end + + # good + case x + in foo if bar + first_method + in foo if baz + second_method + end diff --git a/config/contents/lint/redundant_string_coercion.md b/config/contents/lint/redundant_string_coercion.md index 0a28b282..793ab5ea 100644 --- a/config/contents/lint/redundant_string_coercion.md +++ b/config/contents/lint/redundant_string_coercion.md @@ -1,4 +1,4 @@ -Checks for string conversion in string interpolation, +Checks for string conversion in string interpolation, `print`, `puts`, and `warn` arguments, which is redundant. ### Example: @@ -6,9 +6,15 @@ which is redundant. # bad "result is #{something.to_s}" + print something.to_s + puts something.to_s + warn something.to_s ### Example: # good - "result is #{something}" \ No newline at end of file + "result is #{something}" + print something + puts something + warn something diff --git a/config/contents/lint/to_enum_arguments.md b/config/contents/lint/to_enum_arguments.md index 5873b47b..394f4ffc 100644 --- a/config/contents/lint/to_enum_arguments.md +++ b/config/contents/lint/to_enum_arguments.md @@ -9,6 +9,12 @@ has correct arguments. # good def foo(x, y = 1) + # Alternatives to `__callee__` are `__method__` and `:foo`. return to_enum(__callee__, x, y) - # alternatives to `__callee__` are `__method__` and `:foo` + end + + # good + def foo(x, y = 1) + # It is also allowed if it is wrapped in some method like Sorbet. + return to_enum(T.must(__callee__), x, y) end diff --git a/config/contents/naming/inclusive_language.md b/config/contents/naming/inclusive_language.md index 7ad5c834..0f649ce4 100644 --- a/config/contents/naming/inclusive_language.md +++ b/config/contents/naming/inclusive_language.md @@ -1,5 +1,6 @@ Recommends the use of inclusive language instead of problematic terms. The cop can check the following locations for offenses: + - identifiers - constants - variables @@ -7,6 +8,7 @@ The cop can check the following locations for offenses: - symbols - comments - file paths + Each of these locations can be individually enabled/disabled via configuration, for example CheckIdentifiers = true/false. @@ -17,6 +19,9 @@ An AllowedRegex can be specified for a flagged term to exempt allowed uses of th `WholeWord: true` can be set on a flagged term to indicate the cop should only match when a term matches the whole word (partial matches will not be offenses). +The cop supports autocorrection when there is only one suggestion. When there are multiple +suggestions, the best suggestion cannot be identified and will not be autocorrected. + ### Example: FlaggedTerms: { whitelist: { Suggestions: ['allowlist'] } } # Suggest replacing identifier whitelist with allowlist diff --git a/config/contents/style/class_and_module_children.md b/config/contents/style/class_and_module_children.md index eebeab14..22cbf65f 100644 --- a/config/contents/style/class_and_module_children.md +++ b/config/contents/style/class_and_module_children.md @@ -7,7 +7,7 @@ Autocorrection is unsafe. Moving from compact to nested children requires knowledge of whether the outer parent is a module or a class. Moving from nested to compact requires -verification that the outer parent is defined elsewhere. Rubocop does not +verification that the outer parent is defined elsewhere. RuboCop does not have the knowledge to perform either operation safely and thus requires manual oversight. diff --git a/config/contents/style/class_equality_comparison.md b/config/contents/style/class_equality_comparison.md index 65928cb0..c1a9dee5 100644 --- a/config/contents/style/class_equality_comparison.md +++ b/config/contents/style/class_equality_comparison.md @@ -22,12 +22,16 @@ These are customizable with `AllowedMethods` option. var.class.equal?(Date) var.class.eql?(Date) var.class.name == 'Date' + var.class.to_s == 'Date' + var.class.inspect == 'Date' ### Example: AllowedMethods: [`==`] # good var.instance_of?(Date) var.class == Date var.class.name == 'Date' + var.class.to_s == 'Date' + var.class.inspect == 'Date' # bad var.class.equal?(Date) @@ -42,6 +46,8 @@ These are customizable with `AllowedMethods` option. var.class.equal?(Date) var.class.eql?(Date) var.class.name == 'Date' + var.class.to_s == 'Date' + var.class.inspect == 'Date' ### Example: AllowedPatterns: ['eq'] # good @@ -52,3 +58,5 @@ These are customizable with `AllowedMethods` option. # bad var.class == Date var.class.name == 'Date' + var.class.to_s == 'Date' + var.class.inspect == 'Date' diff --git a/config/contents/style/data_inheritance.md b/config/contents/style/data_inheritance.md new file mode 100644 index 00000000..422609c5 --- /dev/null +++ b/config/contents/style/data_inheritance.md @@ -0,0 +1,21 @@ +Checks for inheritance from `Data.define` to avoid creating the anonymous parent class. + +### Safety: + +Autocorrection is unsafe because it will change the inheritance +tree (e.g. return value of `Module#ancestors`) of the constant. + +### Example: + # bad + class Person < Data.define(:first_name, :last_name) + def age + 42 + end + end + + # good + Person = Data.define(:first_name, :last_name) do + def age + 42 + end + end \ No newline at end of file diff --git a/config/contents/style/file_empty.md b/config/contents/style/file_empty.md index c7de0801..da0ed4e1 100644 --- a/config/contents/style/file_empty.md +++ b/config/contents/style/file_empty.md @@ -2,9 +2,9 @@ Prefer to use `File.empty?('path/to/file')` when checking if a file is empty. ### Safety: -This cop's autocorrection is unsafe it because `File.size`, `File.read`, -and `File.binread` raise `ENOENT` exception when there is no file -corresponding to the path, while `File.empty?` does not raise an exception. +This cop is unsafe, because `File.size`, `File.read`, and `File.binread` +raise `ENOENT` exception when there is no file corresponding to the path, +while `File.empty?` does not raise an exception. ### Example: # bad diff --git a/config/contents/style/hash_syntax.md b/config/contents/style/hash_syntax.md index 7073291e..19cc363b 100644 --- a/config/contents/style/hash_syntax.md +++ b/config/contents/style/hash_syntax.md @@ -82,6 +82,9 @@ The supported styles are: # good {foo: foo, bar: bar} + # good + {foo: foo, bar:} + # good {foo:, bar:} diff --git a/config/contents/style/if_unless_modifier.md b/config/contents/style/if_unless_modifier.md index 9daad5e1..9bd6b639 100644 --- a/config/contents/style/if_unless_modifier.md +++ b/config/contents/style/if_unless_modifier.md @@ -6,6 +6,17 @@ The maximum line length is configured in the `Layout/LineLength` cop. The tab size is configured in the `IndentationWidth` of the `Layout/IndentationStyle` cop. +One-line pattern matching is always allowed. To ensure that there are few cases +where the match variable is not used, and to prevent oversights. The variable `x` +becomes undefined and raises `NameError` when the following example is changed to +the modifier form: + +```ruby +if [42] in [x] + x # `x` is undefined when using modifier form. +end +``` + NOTE: It is allowed when `defined?` argument has an undefined value, because using the modifier form causes the following incompatibility: diff --git a/config/contents/style/method_call_with_args_parentheses.md b/config/contents/style/method_call_with_args_parentheses.md index 46375e3a..94361dbf 100644 --- a/config/contents/style/method_call_with_args_parentheses.md +++ b/config/contents/style/method_call_with_args_parentheses.md @@ -2,21 +2,19 @@ Enforces the presence (default) or absence of parentheses in method calls containing parameters. In the default style (require_parentheses), macro methods are allowed. -Additional methods can be added to the `AllowedMethods` -or `AllowedPatterns` list. These options are -valid only in the default style. Macros can be included by -either setting `IgnoreMacros` to false or adding specific macros to -the `IncludedMacros` list. +Additional methods can be added to the `AllowedMethods` or +`AllowedPatterns` list. These options are valid only in the default +style. Macros can be included by either setting `IgnoreMacros` to false +or adding specific macros to the `IncludedMacros` list. -Precedence of options is all follows: +Precedence of options is as follows: 1. `AllowedMethods` 2. `AllowedPatterns` 3. `IncludedMacros` -eg. If a method is listed in both -`IncludedMacros` and `AllowedMethods`, then the latter takes -precedence (that is, the method is allowed). +If a method is listed in both `IncludedMacros` and `AllowedMethods`, +then the latter takes precedence (that is, the method is allowed). In the alternative style (omit_parentheses), there are three additional options. @@ -35,14 +33,29 @@ options. to `true` allows the presence of parentheses in such a method call even with arguments. -NOTE: Parentheses are still allowed in cases where omitting them -results in ambiguous or syntactically incorrect code. For example, -parentheses are required around a method with arguments when inside an -endless method definition introduced in Ruby 3.0. Parentheses are also -allowed when forwarding arguments with the triple-dot syntax introduced -in Ruby 2.7 as omitting them starts an endless range. -And Ruby 3.1's hash omission syntax has a case that requires parentheses -because of the following issue: https://bugs.ruby-lang.org/issues/18396. +NOTE: The style of `omit_parentheses` allows parentheses in cases where +omitting them results in ambiguous or syntactically incorrect code. + +Non-exhaustive list of examples: + +- Parentheses are required allowed in method calls with arguments inside + literals, logical operators, setting default values in position and + keyword arguments, chaining and more. +- Parentheses are allowed in method calls with arguments inside + operators to avoid ambiguity. + triple-dot syntax introduced in Ruby 2.7 as omitting them starts an + endless range. +- Parentheses are allowed when forwarding arguments with the + triple-dot syntax introduced in Ruby 2.7 as omitting them starts an + endless range. +- Parentheses are required in calls with arguments when inside an + endless method definition introduced in Ruby 3.0. +- Ruby 3.1's hash omission syntax allows parentheses if the method call + is in conditionals and requires parentheses if the call + is not the value-returning expression. See + https://bugs.ruby-lang.org/issues/18396. +- Parentheses are required in anonymous arguments, keyword arguments + and block passing in Ruby 3.2. ### Example: EnforcedStyle: require_parentheses (default) @@ -75,34 +88,28 @@ because of the following issue: https://bugs.ruby-lang.org/issues/18396. array.delete e # bad - foo.enforce(strict: true) + action.enforce(strict: true) # good - foo.enforce strict: true + action.enforce strict: true # good - # Allows parens for calls that won't produce valid Ruby or be ambiguous. - model.validate strict(true) + # Parentheses are allowed for code that can be ambiguous without + # them. + action.enforce(condition) || other_condition # good - # Allows parens for calls that won't produce valid Ruby or be ambiguous. + # Parentheses are allowed for calls that won't produce valid Ruby + # without them. yield path, File.basename(path) # good - # Operators methods calls with parens - array&.[](index) - - # good - # Operators methods without parens, if you prefer - array.[] index - - # good - # Operators methods calls with parens - array&.[](index) - - # good - # Operators methods without parens, if you prefer - array.[] index + # Omitting the parentheses in Ruby 3.1 hash omission syntax can lead + # to ambiguous code. We allow them in conditionals and non-last + # expressions. See https://bugs.ruby-lang.org/issues/18396 + if meets(criteria:, action:) + safe_action(action) || dangerous_action(action) + end ### Example: IgnoreMacros: true (default) diff --git a/config/contents/style/redundant_fetch_block.md b/config/contents/style/redundant_fetch_block.md index a405de51..71c6b217 100644 --- a/config/contents/style/redundant_fetch_block.md +++ b/config/contents/style/redundant_fetch_block.md @@ -1,8 +1,10 @@ -Identifies places where `fetch(key) { value }` -can be replaced by `fetch(key, value)`. +Identifies places where `fetch(key) { value }` can be replaced by `fetch(key, value)`. -In such cases `fetch(key, value)` method is faster -than `fetch(key) { value }`. +In such cases `fetch(key, value)` method is faster than `fetch(key) { value }`. + +NOTE: The block string `'value'` in `hash.fetch(:key) { 'value' }` is detected +when frozen string literal magic comment is enabled (i.e. `# frozen_string_literal: true`), +but not when disabled. ### Safety: diff --git a/config/contents/style/redundant_line_continuation.md b/config/contents/style/redundant_line_continuation.md new file mode 100644 index 00000000..a3b65899 --- /dev/null +++ b/config/contents/style/redundant_line_continuation.md @@ -0,0 +1,60 @@ +Check for redundant line continuation. + +This cop marks a line continuation as redundant if removing the backslash +does not result in a syntax error. +However, a backslash at the end of a comment or +for string concatenation is not redundant and is not considered an offense. + +### Example: + # bad + foo. \ + bar + foo \ + &.bar \ + .baz + + # good + foo. + bar + foo + &.bar + .baz + + # bad + [foo, \ + bar] + {foo: \ + bar} + + # good + [foo, + bar] + {foo: + bar} + + # bad + foo(bar, \ + baz) + + # good + foo(bar, + baz) + + # also good - backslash in string concatenation is not redundant + foo('bar' \ + 'baz') + + # also good - backslash at the end of a comment is not redundant + foo(bar, # \ + baz) + + # also good - backslash at the line following the newline begins with a + or -, + # it is not redundant + 1 \ + + 2 \ + - 3 + + # also good - backslash with newline between the method name and its arguments, + # it is not redundant. + some_method \ + (argument) diff --git a/config/contents/style/unless_logical_operators.md b/config/contents/style/unless_logical_operators.md index 2b320d4c..9f4782ce 100644 --- a/config/contents/style/unless_logical_operators.md +++ b/config/contents/style/unless_logical_operators.md @@ -3,6 +3,7 @@ It discourages such code, as the condition becomes more difficult to read and understand. This cop supports two styles: + - `forbid_mixed_logical_operators` (default) - `forbid_logical_operators`