From ee65081fc37f596ee5aea361a99c98c1f1ed54c7 Mon Sep 17 00:00:00 2001 From: Brian Hawley Date: Fri, 8 Nov 2024 18:33:12 -0600 Subject: [PATCH] Update rubocop to 1.75.2 - General bundle update. - Update the docs too. - Fix rubocop API deprecations. - Fix reasonable rubocop complaints. - Disable unnecessary rubocop complaints. - Explicit ostruct dependency for future compatibility. - Remove an unnecessary empty file. --- .rubocop.yml | 30 ++- .rubocop_todo.yml | 13 ++ .ruby-version | 2 +- Gemfile | 3 +- Gemfile.lock | 171 ++++++++++-------- bin/codeclimate-rubocop | 2 +- .../gemspec/add_runtime_dependency.md | 14 ++ .../contents/gemspec/duplicated_assignment.md | 4 +- .../gemspec/ruby_version_globals_usage.md | 6 +- .../layout/access_modifier_indentation.md | 2 +- config/contents/layout/argument_alignment.md | 3 +- config/contents/layout/array_alignment.md | 2 +- .../contents/layout/assignment_indentation.md | 6 +- config/contents/layout/block_alignment.md | 2 +- config/contents/layout/class_structure.md | 14 +- config/contents/layout/condition_position.md | 4 - .../layout/empty_line_between_defs.md | 6 - .../layout/empty_lines_around_begin_body.md | 12 +- ...ines_around_exception_handling_keywords.md | 2 +- .../layout/first_argument_indentation.md | 4 +- .../layout/first_array_element_indentation.md | 3 - .../layout/first_hash_element_indentation.md | 2 +- .../first_method_argument_line_break.md | 6 + .../layout/first_parameter_indentation.md | 4 +- config/contents/layout/heredoc_indentation.md | 2 +- config/contents/layout/indentation_width.md | 9 +- .../contents/layout/leading_comment_space.md | 37 ++++ config/contents/layout/line_length.md | 40 ++-- .../multiline_method_argument_line_breaks.md | 24 +++ .../multiline_method_call_brace_layout.md | 2 +- ...ultiline_method_definition_brace_layout.md | 2 +- config/contents/layout/parameter_alignment.md | 7 +- .../contents/layout/redundant_line_break.md | 14 +- config/contents/layout/space_after_colon.md | 4 +- config/contents/layout/space_after_comma.md | 2 +- .../contents/layout/space_after_semicolon.md | 2 +- config/contents/layout/space_before_comma.md | 2 +- .../contents/layout/space_before_semicolon.md | 2 +- .../space_inside_array_literal_brackets.md | 6 + .../space_inside_hash_literal_braces.md | 4 + .../lint/ambiguous_block_association.md | 2 - config/contents/lint/ambiguous_operator.md | 2 - .../contents/lint/ambiguous_regexp_literal.md | 2 - .../contents/lint/array_literal_in_regexp.md | 35 ++++ ...binary_operator_with_identical_operands.md | 10 +- config/contents/lint/boolean_symbol.md | 2 - .../lint/circular_argument_reference.md | 15 +- config/contents/lint/constant_reassignment.md | 58 ++++++ config/contents/lint/cop_directive_syntax.md | 30 +++ config/contents/lint/debugger.md | 4 - .../lint/deprecated_open_ssl_constant.md | 10 - config/contents/lint/duplicate_branch.md | 13 ++ .../contents/lint/duplicate_case_condition.md | 4 - config/contents/lint/duplicate_hash_key.md | 4 - config/contents/lint/duplicate_methods.md | 10 - ...uplicate_regexp_character_class_element.md | 2 +- config/contents/lint/duplicate_set_element.md | 33 ++++ .../lint/each_with_object_argument.md | 4 - config/contents/lint/else_layout.md | 2 - .../contents/lint/empty_conditional_body.md | 13 +- config/contents/lint/empty_ensure.md | 12 +- config/contents/lint/empty_expression.md | 2 - config/contents/lint/empty_interpolation.md | 4 - config/contents/lint/empty_when.md | 2 - config/contents/lint/ensure_return.md | 7 +- config/contents/lint/float_comparison.md | 5 +- config/contents/lint/float_out_of_range.md | 6 +- .../lint/format_parameter_mismatch.md | 12 +- ...h_new_with_keyword_arguments_as_default.md | 21 +++ .../lint/implicit_string_concatenation.md | 4 - .../lint/ineffective_access_modifier.md | 7 - config/contents/lint/interpolation_check.md | 4 - config/contents/lint/literal_as_condition.md | 7 +- .../contents/lint/literal_in_interpolation.md | 7 +- config/contents/lint/loop.md | 18 +- config/contents/lint/mixed_case_range.md | 2 +- .../lint/mixed_regexp_capture_types.md | 2 +- .../contents/lint/nested_method_definition.md | 6 - .../contents/lint/next_without_accumulator.md | 4 - .../lint/no_return_in_begin_end_blocks.md | 5 - .../numeric_operation_with_constant_result.md | 39 ++++ .../contents/lint/out_of_range_regexp_ref.md | 2 +- config/contents/lint/percent_string_array.md | 4 - config/contents/lint/percent_symbol_array.md | 4 - config/contents/lint/raise_exception.md | 39 +++- config/contents/lint/rand_one.md | 4 - .../lint/redundant_cop_enable_directive.md | 4 +- .../lint/redundant_regexp_quantifiers.md | 2 +- .../lint/redundant_require_statement.md | 6 - .../lint/redundant_safe_navigation.md | 14 +- .../lint/redundant_splat_expansion.md | 2 +- .../lint/redundant_string_coercion.md | 4 - .../lint/redundant_type_conversion.md | 73 ++++++++ .../lint/refinement_import_methods.md | 2 +- config/contents/lint/require_parentheses.md | 4 - config/contents/lint/rescue_exception.md | 6 +- .../contents/lint/return_in_void_context.md | 2 - config/contents/lint/safe_navigation_chain.md | 4 - .../lint/safe_navigation_consistency.md | 26 ++- .../lint/shadowing_outer_local_variable.md | 14 +- .../contents/lint/shared_mutable_default.md | 41 +++++ ...ppressed_exception_in_number_conversion.md | 34 ++++ .../lint/unescaped_bracket_in_regexp.md | 22 +++ config/contents/lint/unified_integer.md | 4 - config/contents/lint/unreachable_code.md | 5 - .../contents/lint/unused_method_argument.md | 12 +- config/contents/lint/uri_regexp.md | 18 +- config/contents/lint/useless_assignment.md | 16 +- .../contents/lint/useless_constant_scoping.md | 25 +++ config/contents/lint/useless_defined.md | 36 ++++ .../lint/useless_else_without_rescue.md | 4 - .../lint/useless_numeric_operation.md | 25 +++ config/contents/lint/useless_setter_call.md | 4 - config/contents/metrics/block_length.md | 11 +- config/contents/metrics/block_nesting.md | 10 +- config/contents/metrics/class_length.md | 11 +- .../contents/metrics/cyclomatic_complexity.md | 5 +- config/contents/metrics/method_length.md | 11 +- config/contents/metrics/module_length.md | 11 +- .../contents/naming/accessor_method_name.md | 12 +- config/contents/naming/block_forwarding.md | 13 ++ config/contents/naming/method_name.md | 39 +++- config/contents/naming/predicate_name.md | 94 +++++++--- config/contents/naming/variable_name.md | 27 ++- .../style/access_modifier_declarations.md | 26 +++ config/contents/style/accessor_grouping.md | 3 + .../ambiguous_endless_method_definition.md | 23 +++ config/contents/style/arguments_forwarding.md | 13 ++ config/contents/style/array_intersect.md | 13 +- config/contents/style/bitwise_predicate.md | 26 +++ .../style/class_and_module_children.md | 20 +- config/contents/style/collection_compact.md | 4 +- config/contents/style/combinable_defined.md | 18 ++ config/contents/style/combinable_loops.md | 3 + config/contents/style/commented_keyword.md | 4 +- config/contents/style/comparable_between.md | 16 ++ config/contents/style/def_with_parentheses.md | 2 - config/contents/style/dig_chain.md | 19 ++ config/contents/style/each_for_simple_loop.md | 3 +- config/contents/style/empty_literal.md | 6 + config/contents/style/endless_method.md | 113 ++++++++++-- config/contents/style/eval_with_location.md | 22 +-- config/contents/style/exact_regexp_match.md | 2 +- config/contents/style/exponential_notation.md | 2 +- config/contents/style/fetch_env_var.md | 2 +- config/contents/style/file_null.md | 39 ++++ config/contents/style/file_read.md | 7 +- config/contents/style/file_touch.md | 30 +++ config/contents/style/file_write.md | 7 +- config/contents/style/format_string_token.md | 38 +++- config/contents/style/global_std_stream.md | 3 + config/contents/style/hash_each_methods.md | 2 +- config/contents/style/hash_except.md | 38 +++- config/contents/style/hash_fetch_chain.md | 34 ++++ config/contents/style/hash_slice.md | 55 ++++++ config/contents/style/hash_syntax.md | 7 +- .../style/if_with_boolean_literal_branches.md | 1 - config/contents/style/it_assignment.md | 18 ++ config/contents/style/it_block_parameter.md | 33 ++++ .../style/keyword_arguments_merging.md | 14 ++ .../map_compact_with_conditional_block.md | 4 + config/contents/style/map_into_array.md | 14 +- .../method_call_with_args_parentheses.md | 4 +- .../method_call_without_args_parentheses.md | 4 +- .../style/missing_respond_to_missing.md | 35 +++- config/contents/style/mutable_constant.md | 2 +- config/contents/style/next.md | 33 +++- config/contents/style/numeric_predicate.md | 4 +- config/contents/style/one_line_conditional.md | 24 ++- config/contents/style/open_struct_use.md | 8 +- config/contents/style/quoted_symbols.md | 2 +- config/contents/style/raise_args.md | 18 +- config/contents/style/redundant_condition.md | 34 +++- .../redundant_current_directory_in_path.md | 3 +- config/contents/style/redundant_exception.md | 2 +- .../redundant_file_extension_in_require.md | 2 +- config/contents/style/redundant_format.md | 47 +++++ config/contents/style/redundant_freeze.md | 2 +- config/contents/style/redundant_initialize.md | 13 +- .../style/redundant_interpolation_unfreeze.md | 14 ++ .../style/redundant_regexp_character_class.md | 2 +- .../contents/style/redundant_regexp_escape.md | 2 +- .../style/redundant_self_assignment.md | 4 - ...turn_nil_in_predicate_method_definition.md | 21 ++- config/contents/style/safe_navigation.md | 9 +- .../style/safe_navigation_chain_length.md | 19 ++ config/contents/style/select_by_regexp.md | 4 +- .../style/send_with_literal_method_name.md | 13 ++ .../style/single_line_block_params.md | 2 +- .../style/single_line_do_end_block.md | 9 +- config/contents/style/single_line_methods.md | 6 +- config/contents/style/string_methods.md | 2 +- config/contents/style/super_arguments.md | 34 +++- .../style/trailing_comma_in_arguments.md | 5 +- .../style/trailing_comma_in_array_literal.md | 53 +++++- .../style/trailing_comma_in_hash_literal.md | 54 +++++- config/contents/style/while_until_do.md | 2 - config/contents/style/while_until_modifier.md | 2 - lib/cc/engine/file_list_resolver.rb | 10 +- lib/cc/engine/fingerprint.rb | 2 +- lib/cc/engine/issue.rb | 5 +- lib/cc/engine/source_file.rb | 6 +- spec/cc/engine/category_parser_spec.rb | 2 + spec/cc/engine/content_resolver_spec.rb | 6 +- spec/cc/engine/file_list_resolver_spec.rb | 7 +- spec/cc/engine/fingerprint_spec.rb | 4 +- spec/cc/engine/issue_spec.rb | 2 + spec/cc/engine/rubocop_spec.rb | 10 +- spec/cc/engine/source_file_spec.rb | 0 spec/rubocop/config_patch_spec.rb | 7 +- spec/rubocop/cop_patches_spec.rb | 4 +- spec/spec_helper.rb | 2 + spec/support/filesystem_helpers.rb | 2 + spec/support/matchers.rb | 2 + spec/support/rubocop_runner.rb | 4 +- 215 files changed, 2191 insertions(+), 675 deletions(-) create mode 100644 .rubocop_todo.yml create mode 100644 config/contents/gemspec/add_runtime_dependency.md create mode 100644 config/contents/lint/array_literal_in_regexp.md create mode 100644 config/contents/lint/constant_reassignment.md create mode 100644 config/contents/lint/cop_directive_syntax.md create mode 100644 config/contents/lint/duplicate_set_element.md create mode 100644 config/contents/lint/hash_new_with_keyword_arguments_as_default.md create mode 100644 config/contents/lint/numeric_operation_with_constant_result.md create mode 100644 config/contents/lint/redundant_type_conversion.md create mode 100644 config/contents/lint/shared_mutable_default.md create mode 100644 config/contents/lint/suppressed_exception_in_number_conversion.md create mode 100644 config/contents/lint/unescaped_bracket_in_regexp.md create mode 100644 config/contents/lint/useless_constant_scoping.md create mode 100644 config/contents/lint/useless_defined.md create mode 100644 config/contents/lint/useless_numeric_operation.md create mode 100644 config/contents/style/ambiguous_endless_method_definition.md create mode 100644 config/contents/style/bitwise_predicate.md create mode 100644 config/contents/style/combinable_defined.md create mode 100644 config/contents/style/comparable_between.md create mode 100644 config/contents/style/dig_chain.md create mode 100644 config/contents/style/file_null.md create mode 100644 config/contents/style/file_touch.md create mode 100644 config/contents/style/hash_fetch_chain.md create mode 100644 config/contents/style/hash_slice.md create mode 100644 config/contents/style/it_assignment.md create mode 100644 config/contents/style/it_block_parameter.md create mode 100644 config/contents/style/keyword_arguments_merging.md create mode 100644 config/contents/style/redundant_format.md create mode 100644 config/contents/style/redundant_interpolation_unfreeze.md create mode 100644 config/contents/style/safe_navigation_chain_length.md delete mode 100644 spec/cc/engine/source_file_spec.rb diff --git a/.rubocop.yml b/.rubocop.yml index 510e51f2..e0b78f80 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,10 +1,15 @@ -inherit_from: base_rubocop.yml - require: rubocop-rspec +inherit_from: + - .rubocop_todo.yml + - base_rubocop.yml + Layout/MultilineOperationIndentation: Enabled: false +Naming/HeredocDelimiterNaming: + Enabled: false + Naming/FileName: Exclude: - 'bin/codeclimate-rubocop' @@ -20,3 +25,24 @@ Style/TrailingCommaInHashLiteral: Style/TrailingCommaInArguments: Enabled: false + +RSpec/AnyInstance: + Enabled: false + +RSpec/ContextWording: + Enabled: false + +RSpec/DescribedClass: + Enabled: false + +RSpec/ExampleLength: + Enabled: false + +RSpec/MessageChain: + Enabled: false + +RSpec/MultipleExpectations: + Enabled: false + +RSpec/NotToNot: + Enabled: false diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml new file mode 100644 index 00000000..dcc17406 --- /dev/null +++ b/.rubocop_todo.yml @@ -0,0 +1,13 @@ +# This configuration was generated by +# `rubocop --auto-gen-config` +# on 2024-11-08 23:41:07 UTC using RuboCop version 1.68.0. +# The point is for the user to remove these configuration records +# one by one as the offenses are removed from the code base. +# Note that changes in the inspected code, or installation of new +# versions of RuboCop, may require this file to be generated again. + +# Offense count: 1 +# Configuration parameters: AllowComments, AllowNil. +Lint/SuppressedException: + Exclude: + - 'Rakefile' diff --git a/.ruby-version b/.ruby-version index be94e6f5..406ebcbd 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.2.2 +3.2.7 diff --git a/Gemfile b/Gemfile index 6683f2fc..05a62a11 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.64.1", require: false +gem "rubocop", "1.75.2", require: false gem "rubocop-capybara", require: false gem "rubocop-factory_bot", require: false gem "rubocop-graphql", require: false @@ -24,6 +24,7 @@ gem "rubocop-thread_safety", require: false gem "test-prof", require: false group :test do + gem "ostruct", require: false gem "rake" gem "rspec" end diff --git a/Gemfile.lock b/Gemfile.lock index 6ce40714..282469bf 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,127 +1,148 @@ GEM remote: https://rubygems.org/ specs: - activesupport (7.1.3.4) + activesupport (8.0.2) base64 + benchmark (>= 0.3) bigdecimal - concurrent-ruby (~> 1.0, >= 1.0.2) + concurrent-ruby (~> 1.0, >= 1.3.1) connection_pool (>= 2.2.5) drb i18n (>= 1.6, < 2) + logger (>= 1.4.2) minitest (>= 5.1) - mutex_m - tzinfo (~> 2.0) - ast (2.4.2) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + uri (>= 0.13.1) + ast (2.4.3) base64 (0.2.0) - bigdecimal (3.1.8) + benchmark (0.4.0) + bigdecimal (3.1.9) coderay (1.1.3) - concurrent-ruby (1.3.3) - connection_pool (2.4.1) - diff-lcs (1.5.1) + concurrent-ruby (1.3.5) + connection_pool (2.5.1) + diff-lcs (1.6.1) drb (2.2.1) - i18n (1.14.5) + i18n (1.14.7) concurrent-ruby (~> 1.0) - json (2.7.2) - language_server-protocol (3.17.0.3) + json (2.10.2) + language_server-protocol (3.17.0.4) + lint_roller (1.1.0) + logger (1.7.0) method_source (1.1.0) - minitest (5.24.1) - mutex_m (0.2.0) - parallel (1.25.1) - parser (3.3.3.0) + minitest (5.25.5) + ostruct (0.6.1) + parallel (1.27.0) + parser (3.3.8.0) ast (~> 2.4.1) racc - pry (0.14.2) + prism (1.4.0) + pry (0.15.2) coderay (~> 1.1) method_source (~> 1.0) - racc (1.8.0) - rack (3.1.4) + racc (1.8.1) + rack (3.1.13) rainbow (3.1.1) rake (13.2.1) - regexp_parser (2.9.2) - rexml (3.3.1) - strscan + regexp_parser (2.10.0) rspec (3.13.0) rspec-core (~> 3.13.0) rspec-expectations (~> 3.13.0) rspec-mocks (~> 3.13.0) - rspec-core (3.13.0) + rspec-core (3.13.3) rspec-support (~> 3.13.0) - rspec-expectations (3.13.1) + rspec-expectations (3.13.3) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-mocks (3.13.1) + rspec-mocks (3.13.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-support (3.13.1) - rubocop (1.64.1) + rspec-support (3.13.2) + rubocop (1.75.2) json (~> 2.3) - language_server-protocol (>= 3.17.0) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) parallel (~> 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.8, < 3.0) - rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.31.1, < 2.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.44.0, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.31.3) - parser (>= 3.3.1.0) - rubocop-capybara (2.21.0) - rubocop (~> 1.41) - rubocop-factory_bot (2.26.1) - rubocop (~> 1.61) - rubocop-graphql (1.5.2) - rubocop (>= 0.90, < 2) - rubocop-i18n (3.0.0) - rubocop (~> 1.0) - rubocop-minitest (0.35.0) - rubocop (>= 1.61, < 2.0) - rubocop-ast (>= 1.31.1, < 2.0) - rubocop-performance (1.21.1) - rubocop (>= 1.48.1, < 2.0) - rubocop-ast (>= 1.31.1, < 2.0) - rubocop-rails (2.25.1) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.44.1) + parser (>= 3.3.7.2) + prism (~> 1.4) + rubocop-capybara (2.22.1) + lint_roller (~> 1.1) + rubocop (~> 1.72, >= 1.72.1) + rubocop-factory_bot (2.27.1) + lint_roller (~> 1.1) + rubocop (~> 1.72, >= 1.72.1) + rubocop-graphql (1.5.5) + lint_roller (~> 1.1) + rubocop (>= 1.72.1, < 2) + rubocop-i18n (3.2.3) + lint_roller (~> 1.1) + rubocop (>= 1.72.1) + rubocop-minitest (0.38.0) + lint_roller (~> 1.1) + rubocop (>= 1.75.0, < 2.0) + rubocop-ast (>= 1.38.0, < 2.0) + rubocop-performance (1.25.0) + lint_roller (~> 1.1) + rubocop (>= 1.75.0, < 2.0) + rubocop-ast (>= 1.38.0, < 2.0) + rubocop-rails (2.31.0) activesupport (>= 4.2.0) + lint_roller (~> 1.1) rack (>= 1.1) - rubocop (>= 1.33.0, < 2.0) - rubocop-ast (>= 1.31.1, < 2.0) - rubocop-rails-omakase (1.0.0) - rubocop - rubocop-minitest - rubocop-performance - rubocop-rails - rubocop-rake (0.6.0) - rubocop (~> 1.0) - rubocop-rspec (3.0.1) - rubocop (~> 1.61) - rubocop-rspec_rails (2.30.0) - rubocop (~> 1.61) - rubocop-rspec (~> 3, >= 3.0.1) - rubocop-sequel (0.3.4) - rubocop (~> 1.0) - rubocop-shopify (2.15.1) - rubocop (~> 1.51) - rubocop-sorbet (0.8.3) - rubocop (>= 0.90.0) - rubocop-thread_safety (0.5.1) - rubocop (>= 0.90.0) + rubocop (>= 1.75.0, < 2.0) + rubocop-ast (>= 1.38.0, < 2.0) + rubocop-rails-omakase (1.1.0) + rubocop (>= 1.72) + rubocop-performance (>= 1.24) + rubocop-rails (>= 2.30) + rubocop-rake (0.7.1) + lint_roller (~> 1.1) + rubocop (>= 1.72.1) + rubocop-rspec (3.5.0) + lint_roller (~> 1.1) + rubocop (~> 1.72, >= 1.72.1) + rubocop-rspec_rails (2.31.0) + lint_roller (~> 1.1) + rubocop (~> 1.72, >= 1.72.1) + rubocop-rspec (~> 3.5) + rubocop-sequel (0.4.1) + lint_roller (~> 1.1) + rubocop (>= 1.72.1, < 2) + rubocop-shopify (2.16.0) + rubocop (~> 1.62) + rubocop-sorbet (0.10.0) + rubocop (>= 1) + rubocop-thread_safety (0.7.2) + lint_roller (~> 1.1) + rubocop (~> 1.72, >= 1.72.1) ruby-progressbar (1.13.0) - strscan (3.1.0) - test-prof (1.3.3.1) + securerandom (0.4.1) + test-prof (1.4.4) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - unicode-display_width (2.5.0) + unicode-display_width (3.1.4) + unicode-emoji (~> 4.0, >= 4.0.4) + unicode-emoji (4.0.4) + uri (1.0.3) PLATFORMS ruby DEPENDENCIES activesupport + ostruct parser pry rake rspec - rubocop (= 1.64.1) + rubocop (= 1.75.2) rubocop-capybara rubocop-factory_bot rubocop-graphql diff --git a/bin/codeclimate-rubocop b/bin/codeclimate-rubocop index 7bca1975..6f36157f 100755 --- a/bin/codeclimate-rubocop +++ b/bin/codeclimate-rubocop @@ -14,4 +14,4 @@ engine_config = {} end -CC::Engine::Rubocop.new(Dir.pwd, engine_config, STDOUT).run +CC::Engine::Rubocop.new(Dir.pwd, engine_config, $stdout).run diff --git a/config/contents/gemspec/add_runtime_dependency.md b/config/contents/gemspec/add_runtime_dependency.md new file mode 100644 index 00000000..8605709d --- /dev/null +++ b/config/contents/gemspec/add_runtime_dependency.md @@ -0,0 +1,14 @@ +Prefer `add_dependency` over `add_runtime_dependency` as the latter is +considered soft-deprecated. + +### Example: + + # bad + Gem::Specification.new do |spec| + spec.add_runtime_dependency('rubocop') + end + + # good + Gem::Specification.new do |spec| + spec.add_dependency('rubocop') + end diff --git a/config/contents/gemspec/duplicated_assignment.md b/config/contents/gemspec/duplicated_assignment.md index 6afaac86..a51a6e95 100644 --- a/config/contents/gemspec/duplicated_assignment.md +++ b/config/contents/gemspec/duplicated_assignment.md @@ -26,6 +26,6 @@ permitted because it is the intended use of appending values. # good Gem::Specification.new do |spec| - spec.add_runtime_dependency('parallel', '~> 1.10') - spec.add_runtime_dependency('parser', '>= 2.3.3.1', '< 3.0') + spec.add_dependency('parallel', '~> 1.10') + spec.add_dependency('parser', '>= 2.3.3.1', '< 3.0') end \ No newline at end of file diff --git a/config/contents/gemspec/ruby_version_globals_usage.md b/config/contents/gemspec/ruby_version_globals_usage.md index 953f14f8..19aed44d 100644 --- a/config/contents/gemspec/ruby_version_globals_usage.md +++ b/config/contents/gemspec/ruby_version_globals_usage.md @@ -9,13 +9,13 @@ to execute `rake release` and not user's ruby version. # bad Gem::Specification.new do |spec| if RUBY_VERSION >= '3.0' - spec.add_runtime_dependency 'gem_a' + spec.add_dependency 'gem_a' else - spec.add_runtime_dependency 'gem_b' + spec.add_dependency 'gem_b' end end # good Gem::Specification.new do |spec| - spec.add_runtime_dependency 'gem_a' + spec.add_dependency 'gem_a' end diff --git a/config/contents/layout/access_modifier_indentation.md b/config/contents/layout/access_modifier_indentation.md index 096ab569..c999e977 100644 --- a/config/contents/layout/access_modifier_indentation.md +++ b/config/contents/layout/access_modifier_indentation.md @@ -1,5 +1,5 @@ Bare access modifiers (those not applying to specific methods) should be -indented as deep as method definitions, or as deep as the class/module +indented as deep as method definitions, or as deep as the `class`/`module` keyword, depending on configuration. ### Example: EnforcedStyle: indent (default) diff --git a/config/contents/layout/argument_alignment.md b/config/contents/layout/argument_alignment.md index 4165f3e6..94980e8f 100644 --- a/config/contents/layout/argument_alignment.md +++ b/config/contents/layout/argument_alignment.md @@ -1,5 +1,4 @@ -Here we check if the arguments on a multi-line method -definition are aligned. +Check that the arguments on a multi-line method call are aligned. ### Example: EnforcedStyle: with_first_argument (default) # good diff --git a/config/contents/layout/array_alignment.md b/config/contents/layout/array_alignment.md index c80a71f9..6d5a2c56 100644 --- a/config/contents/layout/array_alignment.md +++ b/config/contents/layout/array_alignment.md @@ -1,4 +1,4 @@ -Here we check if the elements of a multi-line array literal are +Check that the elements of a multi-line array literal are aligned. ### Example: EnforcedStyle: with_first_element (default) diff --git a/config/contents/layout/assignment_indentation.md b/config/contents/layout/assignment_indentation.md index dfe6f902..eb9166a9 100644 --- a/config/contents/layout/assignment_indentation.md +++ b/config/contents/layout/assignment_indentation.md @@ -1,6 +1,9 @@ Checks the indentation of the first line of the right-hand-side of a multi-line assignment. +The indentation of the remaining lines can be corrected with +other cops such as `Layout/IndentationConsistency` and `Layout/EndAlignment`. + ### Example: # bad value = @@ -13,6 +16,3 @@ right-hand-side of a multi-line assignment. if foo 'bar' end - -The indentation of the remaining lines can be corrected with -other cops such as `IndentationConsistency` and `EndAlignment`. \ No newline at end of file diff --git a/config/contents/layout/block_alignment.md b/config/contents/layout/block_alignment.md index bc7e04f8..870c2fe6 100644 --- a/config/contents/layout/block_alignment.md +++ b/config/contents/layout/block_alignment.md @@ -11,7 +11,7 @@ start of the line where the `do` appeared. start of the line where the expression started. `either` (which is the default) : the `end` is allowed to be in either -location. The autofixer will default to `start_of_line`. +location. The autocorrect will default to `start_of_line`. ### Example: EnforcedStyleAlignWith: either (default) # bad diff --git a/config/contents/layout/class_structure.md b/config/contents/layout/class_structure.md index a6319e86..c6c6d373 100644 --- a/config/contents/layout/class_structure.md +++ b/config/contents/layout/class_structure.md @@ -1,20 +1,20 @@ -Checks if the code style follows the ExpectedOrder configuration: +Checks if the code style follows the `ExpectedOrder` configuration: `Categories` allows us to map macro names into a category. Consider an example of code style that covers the following order: -* Module inclusion (include, prepend, extend) +* Module inclusion (`include`, `prepend`, `extend`) * Constants -* Associations (has_one, has_many) -* Public attribute macros (attr_accessor, attr_writer, attr_reader) -* Other macros (validates, validate) +* Associations (`has_one`, `has_many`) +* Public attribute macros (`attr_accessor`, `attr_writer`, `attr_reader`) +* Other macros (`validates`, `validate`) * Public class methods * Initializer * Public instance methods -* Protected attribute macros (attr_accessor, attr_writer, attr_reader) +* Protected attribute macros (`attr_accessor`, `attr_writer`, `attr_reader`) * Protected instance methods -* Private attribute macros (attr_accessor, attr_writer, attr_reader) +* Private attribute macros (`attr_accessor`, `attr_writer`, `attr_reader`) * Private instance methods You can configure the following order: diff --git a/config/contents/layout/condition_position.md b/config/contents/layout/condition_position.md index fd49e4b7..c9270184 100644 --- a/config/contents/layout/condition_position.md +++ b/config/contents/layout/condition_position.md @@ -4,16 +4,12 @@ if/while/until. ### Example: # bad - if some_condition do_something end -### Example: - # good - if some_condition do_something end \ No newline at end of file diff --git a/config/contents/layout/empty_line_between_defs.md b/config/contents/layout/empty_line_between_defs.md index 6b88bd43..ea30211a 100644 --- a/config/contents/layout/empty_line_between_defs.md +++ b/config/contents/layout/empty_line_between_defs.md @@ -17,8 +17,6 @@ one-line definitions are considered an offense. def b end -### Example: - # good def a end @@ -37,8 +35,6 @@ one-line definitions are considered an offense. def b end -### Example: - # good class A end @@ -60,8 +56,6 @@ one-line definitions are considered an offense. def b end -### Example: - # good module A end diff --git a/config/contents/layout/empty_lines_around_begin_body.md b/config/contents/layout/empty_lines_around_begin_body.md index 3998bc19..91b5e457 100644 --- a/config/contents/layout/empty_lines_around_begin_body.md +++ b/config/contents/layout/empty_lines_around_begin_body.md @@ -3,16 +3,14 @@ blocks. ### Example: - # good - + # bad begin + # ... - end - # bad + end + # good begin - # ... - - end \ No newline at end of file + end diff --git a/config/contents/layout/empty_lines_around_exception_handling_keywords.md b/config/contents/layout/empty_lines_around_exception_handling_keywords.md index 097b2f00..9aba70b1 100644 --- a/config/contents/layout/empty_lines_around_exception_handling_keywords.md +++ b/config/contents/layout/empty_lines_around_exception_handling_keywords.md @@ -1,7 +1,7 @@ Checks if empty lines exist around the bodies of `begin` sections. This cop doesn't check empty lines at `begin` body beginning/end and around method definition body. -`Style/EmptyLinesAroundBeginBody` or `Style/EmptyLinesAroundMethodBody` +`Layout/EmptyLinesAroundBeginBody` or `Layout/EmptyLinesAroundMethodBody` can be used for this purpose. ### Example: diff --git a/config/contents/layout/first_argument_indentation.md b/config/contents/layout/first_argument_indentation.md index 4164f922..3c0822a4 100644 --- a/config/contents/layout/first_argument_indentation.md +++ b/config/contents/layout/first_argument_indentation.md @@ -113,8 +113,8 @@ This cop will respect `Layout/ArgumentAlignment` and will not work when ### Example: EnforcedStyle: special_for_inner_method_call # The first argument should normally be indented one step more than - # the preceding line, but if it's a argument for a method call that - # is itself a argument in a method call, then the inner argument + # the preceding line, but if it's an argument for a method call that + # is itself an argument in a method call, then the inner argument # should be indented relative to the inner method. # good diff --git a/config/contents/layout/first_array_element_indentation.md b/config/contents/layout/first_array_element_indentation.md index 0e031b98..bb7bf464 100644 --- a/config/contents/layout/first_array_element_indentation.md +++ b/config/contents/layout/first_array_element_indentation.md @@ -46,7 +46,6 @@ styles are 'consistent' and 'align_brackets'. Here are examples: # defined inside a method call. # bad - # consistent array = [ :value ] @@ -67,13 +66,11 @@ styles are 'consistent' and 'align_brackets'. Here are examples: # brackets are indented to the same position. # bad - # align_brackets and_now_for_something = [ :completely_different ] # good - # align_brackets and_now_for_something = [ :completely_different ] \ No newline at end of file diff --git a/config/contents/layout/first_hash_element_indentation.md b/config/contents/layout/first_hash_element_indentation.md index 907572d8..6c864bf0 100644 --- a/config/contents/layout/first_hash_element_indentation.md +++ b/config/contents/layout/first_hash_element_indentation.md @@ -2,7 +2,7 @@ Checks the indentation of the first key in a hash literal where the opening brace and the first key are on separate lines. The other keys' indentations are handled by the HashAlignment cop. -By default, Hash literals that are arguments in a method call with +By default, `Hash` literals that are arguments in a method call with parentheses, and where the opening curly brace of the hash is on the same line as the opening parenthesis of the method call, shall have their first key indented one step (two spaces) more than the position diff --git a/config/contents/layout/first_method_argument_line_break.md b/config/contents/layout/first_method_argument_line_break.md index 62a1936b..d30c0e3b 100644 --- a/config/contents/layout/first_method_argument_line_break.md +++ b/config/contents/layout/first_method_argument_line_break.md @@ -57,3 +57,9 @@ multi-line method call. qux: "b", } ) + +### Example: AllowedMethods: ['some_method'] + + # good + some_method(foo, bar, + baz) \ No newline at end of file diff --git a/config/contents/layout/first_parameter_indentation.md b/config/contents/layout/first_parameter_indentation.md index 2aa518fb..d3e5d569 100644 --- a/config/contents/layout/first_parameter_indentation.md +++ b/config/contents/layout/first_parameter_indentation.md @@ -1,9 +1,9 @@ Checks the indentation of the first parameter in a method definition. Parameters after the first one are checked by -Layout/ParameterAlignment, not by this cop. +`Layout/ParameterAlignment`, not by this cop. For indenting the first argument of method _calls_, check out -Layout/FirstArgumentIndentation, which supports options related to +`Layout/FirstArgumentIndentation`, which supports options related to nesting that are irrelevant for method _definitions_. ### Example: diff --git a/config/contents/layout/heredoc_indentation.md b/config/contents/layout/heredoc_indentation.md index fdb29f2e..e63f5ee2 100644 --- a/config/contents/layout/heredoc_indentation.md +++ b/config/contents/layout/heredoc_indentation.md @@ -1,7 +1,7 @@ Checks the indentation of the here document bodies. The bodies are indented one step. -Note: When ``Layout/LineLength``'s `AllowHeredoc` is false (not default), +NOTE: When ``Layout/LineLength``'s `AllowHeredoc` is false (not default), this cop does not add any offenses for long here documents to avoid ``Layout/LineLength``'s offenses. diff --git a/config/contents/layout/indentation_width.md b/config/contents/layout/indentation_width.md index 1787a56b..b91ca7c4 100644 --- a/config/contents/layout/indentation_width.md +++ b/config/contents/layout/indentation_width.md @@ -1,10 +1,9 @@ -Checks for indentation that doesn't use the specified number -of spaces. +Checks for indentation that doesn't use the specified number of spaces. +The indentation width can be configured using the `Width` setting. The default width is 2. -See also the IndentationConsistency cop which is the companion to this -one. +See also the `Layout/IndentationConsistency` cop which is the companion to this one. -### Example: +### Example: Width: 2 (default) # bad class A def test diff --git a/config/contents/layout/leading_comment_space.md b/config/contents/layout/leading_comment_space.md index f9d9122a..22d2fb9d 100644 --- a/config/contents/layout/leading_comment_space.md +++ b/config/contents/layout/leading_comment_space.md @@ -43,3 +43,40 @@ or rackup options. #ruby=2.7.0 #ruby-gemset=myproject + +### Example: AllowRBSInlineAnnotation: false (default) + + # bad + + include Enumerable #[Integer] + + attr_reader :name #: String + attr_reader :age #: Integer? + +### Example: AllowRBSInlineAnnotation: true + + # good + + include Enumerable #[Integer] + + attr_reader :name #: String + attr_reader :age #: Integer? + +### Example: AllowSteepAnnotation: false (default) + + # bad + [1, 2, 3].each_with_object([]) do |n, list| #$ Array[Integer] + list << n + end + + name = 'John' #: String + +### Example: AllowSteepAnnotation: true + + # good + + [1, 2, 3].each_with_object([]) do |n, list| #$ Array[Integer] + list << n + end + + name = 'John' #: String diff --git a/config/contents/layout/line_length.md b/config/contents/layout/line_length.md index e98004ee..ab114915 100644 --- a/config/contents/layout/line_length.md +++ b/config/contents/layout/line_length.md @@ -10,29 +10,29 @@ inserting line breaks into expressions that can be safely split across lines. These include arrays, hashes, and method calls with argument lists. -If autocorrection is enabled, the following Layout cops +If autocorrection is enabled, the following cops are recommended to further format the broken lines. (Many of these are enabled by default.) -* ArgumentAlignment -* ArrayAlignment -* BlockAlignment -* BlockDelimiters -* BlockEndNewline -* ClosingParenthesisIndentation -* FirstArgumentIndentation -* FirstArrayElementIndentation -* FirstHashElementIndentation -* FirstParameterIndentation -* HashAlignment -* IndentationWidth -* MultilineArrayLineBreaks -* MultilineBlockLayout -* MultilineHashBraceLayout -* MultilineHashKeyLineBreaks -* MultilineMethodArgumentLineBreaks -* MultilineMethodParameterLineBreaks -* ParameterAlignment +* `Layout/ArgumentAlignment` +* `Layout/ArrayAlignment` +* `Layout/BlockAlignment` +* `Layout/BlockEndNewline` +* `Layout/ClosingParenthesisIndentation` +* `Layout/FirstArgumentIndentation` +* `Layout/FirstArrayElementIndentation` +* `Layout/FirstHashElementIndentation` +* `Layout/FirstParameterIndentation` +* `Layout/HashAlignment` +* `Layout/IndentationWidth` +* `Layout/MultilineArrayLineBreaks` +* `Layout/MultilineBlockLayout` +* `Layout/MultilineHashBraceLayout` +* `Layout/MultilineHashKeyLineBreaks` +* `Layout/MultilineMethodArgumentLineBreaks` +* `Layout/MultilineMethodParameterLineBreaks` +* `Layout/ParameterAlignment` +* `Style/BlockDelimiters` Together, these cops will pretty print hashes, arrays, method calls, etc. For example, let's say the max columns diff --git a/config/contents/layout/multiline_method_argument_line_breaks.md b/config/contents/layout/multiline_method_argument_line_breaks.md index 914c1fac..6203989f 100644 --- a/config/contents/layout/multiline_method_argument_line_breaks.md +++ b/config/contents/layout/multiline_method_argument_line_breaks.md @@ -28,6 +28,18 @@ be on a separate line, see `Layout/FirstMethodArgumentLineBreak`. ### Example: AllowMultilineFinalElement: false (default) + # bad + foo(a, b, + c + ) + + # bad + foo( + a, b, { + foo: "bar", + } + ) + # good foo( a, @@ -39,6 +51,18 @@ 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, diff --git a/config/contents/layout/multiline_method_call_brace_layout.md b/config/contents/layout/multiline_method_call_brace_layout.md index 57a611ab..18ebea27 100644 --- a/config/contents/layout/multiline_method_call_brace_layout.md +++ b/config/contents/layout/multiline_method_call_brace_layout.md @@ -7,7 +7,7 @@ If a method call's opening brace is on the same line as the first argument of the call, then the closing brace should be on the same line as the last argument of the call. -If an method call's opening brace is on the line above the first +If a method call's opening brace is on the line above the first argument of the call, then the closing brace should be on the line below the last argument of the call. diff --git a/config/contents/layout/multiline_method_definition_brace_layout.md b/config/contents/layout/multiline_method_definition_brace_layout.md index 3e1dde6f..20c1b592 100644 --- a/config/contents/layout/multiline_method_definition_brace_layout.md +++ b/config/contents/layout/multiline_method_definition_brace_layout.md @@ -7,7 +7,7 @@ If a method definition's opening brace is on the same line as the first parameter of the definition, then the closing brace should be on the same line as the last parameter of the definition. -If an method definition's opening brace is on the line above the first +If a method definition's opening brace is on the line above the first parameter of the definition, then the closing brace should be on the line below the last parameter of the definition. diff --git a/config/contents/layout/parameter_alignment.md b/config/contents/layout/parameter_alignment.md index ec3d2d29..08d86cd6 100644 --- a/config/contents/layout/parameter_alignment.md +++ b/config/contents/layout/parameter_alignment.md @@ -1,8 +1,7 @@ -Here we check if the parameters on a multi-line method call or -definition are aligned. +Check that the parameters on a multi-line method call or definition are aligned. -To set the alignment of the first argument, use the cop -FirstParameterIndentation. +To set the alignment of the first argument, use the +`Layout/FirstParameterIndentation` cop. ### Example: EnforcedStyle: with_first_parameter (default) # good diff --git a/config/contents/layout/redundant_line_break.md b/config/contents/layout/redundant_line_break.md index 4cb540fa..3148cbaf 100644 --- a/config/contents/layout/redundant_line_break.md +++ b/config/contents/layout/redundant_line_break.md @@ -1,25 +1,29 @@ Checks whether certain expressions, e.g. method calls, that could fit completely on a single line, are broken up into multiple lines unnecessarily. -### Example: any configuration +### Example: # bad foo( a, b ) + # good + foo(a, b) + + # bad puts 'string that fits on ' \ 'a single line' + # good + puts 'string that fits on a single line' + + # bad things .select { |thing| thing.cond? } .join('-') # good - foo(a, b) - - puts 'string that fits on a single line' - things.select { |thing| thing.cond? }.join('-') ### Example: InspectBlocks: false (default) diff --git a/config/contents/layout/space_after_colon.md b/config/contents/layout/space_after_colon.md index 1e9469b1..61d826c7 100644 --- a/config/contents/layout/space_after_colon.md +++ b/config/contents/layout/space_after_colon.md @@ -1,6 +1,6 @@ -Checks for colon (:) not followed by some kind of space. +Checks for colon (`:`) not followed by some kind of space. N.B. this cop does not handle spaces after a ternary operator, which are -instead handled by Layout/SpaceAroundOperators. +instead handled by `Layout/SpaceAroundOperators`. ### Example: # bad diff --git a/config/contents/layout/space_after_comma.md b/config/contents/layout/space_after_comma.md index 80d94232..3071066e 100644 --- a/config/contents/layout/space_after_comma.md +++ b/config/contents/layout/space_after_comma.md @@ -1,4 +1,4 @@ -Checks for comma (,) not followed by some kind of space. +Checks for comma (`,`) not followed by some kind of space. ### Example: diff --git a/config/contents/layout/space_after_semicolon.md b/config/contents/layout/space_after_semicolon.md index c37ae73b..287aa3cb 100644 --- a/config/contents/layout/space_after_semicolon.md +++ b/config/contents/layout/space_after_semicolon.md @@ -1,4 +1,4 @@ -Checks for semicolon (;) not followed by some kind of space. +Checks for semicolon (`;`) not followed by some kind of space. ### Example: # bad diff --git a/config/contents/layout/space_before_comma.md b/config/contents/layout/space_before_comma.md index 3dda1d58..46fc590f 100644 --- a/config/contents/layout/space_before_comma.md +++ b/config/contents/layout/space_before_comma.md @@ -1,4 +1,4 @@ -Checks for comma (,) preceded by space. +Checks for comma (`,`) preceded by space. ### Example: # bad diff --git a/config/contents/layout/space_before_semicolon.md b/config/contents/layout/space_before_semicolon.md index c45f1030..d72ee09d 100644 --- a/config/contents/layout/space_before_semicolon.md +++ b/config/contents/layout/space_before_semicolon.md @@ -1,4 +1,4 @@ -Checks for semicolon (;) preceded by space. +Checks for semicolon (`;`) preceded by space. ### Example: # bad diff --git a/config/contents/layout/space_inside_array_literal_brackets.md b/config/contents/layout/space_inside_array_literal_brackets.md index 0f3225e2..80a2497b 100644 --- a/config/contents/layout/space_inside_array_literal_brackets.md +++ b/config/contents/layout/space_inside_array_literal_brackets.md @@ -7,9 +7,11 @@ surrounding space depending on configuration. # bad array = [ a, b, c, d ] + array = [ a, [ b, c ]] # good array = [a, b, c, d] + array = [a, [b, c]] ### Example: EnforcedStyle: space # The `space` style enforces that array literals have @@ -17,9 +19,11 @@ surrounding space depending on configuration. # bad array = [a, b, c, d] + array = [ a, [ b, c ]] # good array = [ a, b, c, d ] + array = [ a, [ b, c ] ] ### Example: EnforcedStyle: compact # The `compact` style normally requires a space inside @@ -27,6 +31,7 @@ surrounding space depending on configuration. # or right brackets are collapsed together in nested arrays. # bad + array = [a, b, c, d] array = [ a, [ b, c ] ] array = [ [ a ], @@ -34,6 +39,7 @@ surrounding space depending on configuration. ] # good + array = [ a, b, c, d ] array = [ a, [ b, c ]] array = [[ a ], [ b, c ]] diff --git a/config/contents/layout/space_inside_hash_literal_braces.md b/config/contents/layout/space_inside_hash_literal_braces.md index af63a47a..559b0e5c 100644 --- a/config/contents/layout/space_inside_hash_literal_braces.md +++ b/config/contents/layout/space_inside_hash_literal_braces.md @@ -7,9 +7,11 @@ surrounding space depending on configuration. # bad h = {a: 1, b: 2} + foo = {{ a: 1 } => { b: { c: 2 }}} # good h = { a: 1, b: 2 } + foo = { { a: 1 } => { b: { c: 2 } } } ### Example: EnforcedStyle: no_space # The `no_space` style enforces that hash literals have @@ -17,9 +19,11 @@ surrounding space depending on configuration. # bad h = { a: 1, b: 2 } + foo = {{ a: 1 } => { b: { c: 2 }}} # good h = {a: 1, b: 2} + foo = {{a: 1} => {b: {c: 2}}} ### Example: EnforcedStyle: compact # The `compact` style normally requires a space inside diff --git a/config/contents/lint/ambiguous_block_association.md b/config/contents/lint/ambiguous_block_association.md index f7baee73..da639c17 100644 --- a/config/contents/lint/ambiguous_block_association.md +++ b/config/contents/lint/ambiguous_block_association.md @@ -9,8 +9,6 @@ By default, there are no methods to allowed. # bad some_method a { |val| puts val } -### Example: - # good # With parentheses, there's no ambiguity. some_method(a { |val| puts val }) diff --git a/config/contents/lint/ambiguous_operator.md b/config/contents/lint/ambiguous_operator.md index 99b3b9e5..5584c1d7 100644 --- a/config/contents/lint/ambiguous_operator.md +++ b/config/contents/lint/ambiguous_operator.md @@ -9,8 +9,6 @@ method invocation without parentheses. # a `*` method invocation (i.e. `do_something.*(some_array)`). do_something *some_array -### Example: - # good # With parentheses, there's no ambiguity. diff --git a/config/contents/lint/ambiguous_regexp_literal.md b/config/contents/lint/ambiguous_regexp_literal.md index 7ad32663..0a2fa316 100644 --- a/config/contents/lint/ambiguous_regexp_literal.md +++ b/config/contents/lint/ambiguous_regexp_literal.md @@ -10,8 +10,6 @@ a method invocation without parentheses. # (i.e. `do_something./(pattern)./(i)`) do_something /pattern/i -### Example: - # good # With parentheses, there's no ambiguity. diff --git a/config/contents/lint/array_literal_in_regexp.md b/config/contents/lint/array_literal_in_regexp.md new file mode 100644 index 00000000..564cee50 --- /dev/null +++ b/config/contents/lint/array_literal_in_regexp.md @@ -0,0 +1,35 @@ +Checks for an array literal interpolated inside a regexp. + +When interpolating an array literal, it is converted to a string. This means +that when inside a regexp, it acts as a character class but with additional +quotes, spaces and commas that are likely not intended. For example, +`/#{%w[a b c]}/` parses as `/["a", "b", "c"]/` (or `/["a, bc]/` without +repeated characters). + +The cop can autocorrect to a character class (if all items in the array are a +single character) or alternation (if the array contains longer items). + +NOTE: This only considers interpolated arrays that contain only strings, symbols, +integers, and floats. Any other type is not easily convertible to a character class +or regexp alternation. + +### Safety: + +Autocorrection is unsafe because it will change the regexp pattern, by +removing the additional quotes, spaces and commas from the character class. + +### Example: + # bad + /#{%w[a b c]}/ + + # good + /[abc]/ + + # bad + /#{%w[foo bar baz]}/ + + # good + /(?:foo|bar|baz)/ + + # bad - construct a regexp rather than interpolate an array of identifiers + /#{[foo, bar]}/ diff --git a/config/contents/lint/binary_operator_with_identical_operands.md b/config/contents/lint/binary_operator_with_identical_operands.md index 619c8e00..0ce6070c 100644 --- a/config/contents/lint/binary_operator_with_identical_operands.md +++ b/config/contents/lint/binary_operator_with_identical_operands.md @@ -1,16 +1,15 @@ Checks for places where binary operator has identical operands. -It covers arithmetic operators: `-`, `/`, `%`; -comparison operators: `==`, `===`, `=~`, `>`, `>=`, `<`, ``<=``; +It covers comparison operators: `==`, `===`, `=~`, `>`, `>=`, `<`, ``<=``; bitwise operators: `|`, `^`, `&`; boolean operators: `&&`, `||` and "spaceship" operator - ``<=>``. Simple arithmetic operations are allowed by this cop: `+`, `*`, `**`, `<<` and `>>`. Although these can be rewritten in a different way, it should not be necessary to -do so. This does not include operations such as `-` or `/` where the result will -always be the same (`x - x` will always be 0; `x / x` will always be 1), and -thus are legitimate offenses. +do so. Operations such as `-` or `/` where the result will always be the same +(`x - x` will always be 0; `x / x` will always be 1) are offenses, but these +are covered by `Lint/NumericOperationWithConstantResult` instead. ### Safety: @@ -25,7 +24,6 @@ end ### Example: # bad - x / x x.top >= x.top if a.x != 0 && a.x != 0 diff --git a/config/contents/lint/boolean_symbol.md b/config/contents/lint/boolean_symbol.md index f677838e..b5d8304f 100644 --- a/config/contents/lint/boolean_symbol.md +++ b/config/contents/lint/boolean_symbol.md @@ -15,8 +15,6 @@ changed to actual booleans. # good true -### Example: - # bad :false diff --git a/config/contents/lint/circular_argument_reference.md b/config/contents/lint/circular_argument_reference.md index f8a70312..e8d40bfd 100644 --- a/config/contents/lint/circular_argument_reference.md +++ b/config/contents/lint/circular_argument_reference.md @@ -3,42 +3,31 @@ arguments and optional ordinal arguments. This cop mirrors a warning produced by MRI since 2.2. +NOTE: This syntax is no longer valid on Ruby 2.7 or higher. + ### Example: # bad - def bake(pie: pie) pie.heat_up end -### Example: - # good - def bake(pie:) pie.refrigerate end -### Example: - # good - def bake(pie: self.pie) pie.feed_to(user) end -### Example: - # bad - def cook(dry_ingredients = dry_ingredients) dry_ingredients.reduce(&:+) end -### Example: - # good - def cook(dry_ingredients = self.dry_ingredients) dry_ingredients.combine end \ No newline at end of file diff --git a/config/contents/lint/constant_reassignment.md b/config/contents/lint/constant_reassignment.md new file mode 100644 index 00000000..7bc3046f --- /dev/null +++ b/config/contents/lint/constant_reassignment.md @@ -0,0 +1,58 @@ +Checks for constant reassignments. + +Emulates Ruby's runtime warning "already initialized constant X" +when a constant is reassigned in the same file and namespace using the +`NAME = value` syntax. + +The cop cannot catch all offenses, like, for example, when a constant +is reassigned in another file, or when using metaprogramming (`Module#const_set`). + +The cop only takes into account constants assigned in a "simple" way: directly +inside class/module definition, or within another constant. Other type of assignments +(e.g., inside a conditional) are disregarded. + +The cop also tracks constant removal using `Module#remove_const` with symbol +or string argument. + +### Example: + # bad + X = :foo + X = :bar + + # bad + class A + X = :foo + X = :bar + end + + # bad + module A + X = :foo + X = :bar + end + + # good - keep only one assignment + X = :bar + + class A + X = :bar + end + + module A + X = :bar + end + + # good - use OR assignment + X = :foo + X ||= :bar + + # good - use conditional assignment + X = :foo + X = :bar unless defined?(X) + + # good - remove the assigned constant first + class A + X = :foo + remove_const :X + X = :bar + end diff --git a/config/contents/lint/cop_directive_syntax.md b/config/contents/lint/cop_directive_syntax.md new file mode 100644 index 00000000..e490227f --- /dev/null +++ b/config/contents/lint/cop_directive_syntax.md @@ -0,0 +1,30 @@ +Checks that `# rubocop:enable ...` and `# rubocop:disable ...` statements +are strictly formatted. + +A comment can be added to the directive by prefixing it with `--`. + +### Example: + # bad + # rubocop:disable Layout/LineLength Style/Encoding + # ^ missing comma + + # bad + # rubocop:disable + + # bad + # rubocop:disable Layout/LineLength # rubocop:disable Style/Encoding + + # bad + # rubocop:wrongmode Layout/LineLength + + # good + # rubocop:disable Layout/LineLength + + # good + # rubocop:disable Layout/LineLength, Style/Encoding + + # good + # rubocop:disable all + + # good + # rubocop:disable Layout/LineLength -- This is a good comment. diff --git a/config/contents/lint/debugger.md b/config/contents/lint/debugger.md index a36224e0..a79583d5 100644 --- a/config/contents/lint/debugger.md +++ b/config/contents/lint/debugger.md @@ -37,8 +37,6 @@ be configured through `DebuggerRequires`. It has the same structure as do_something end -### Example: - # bad (ok during development) # using byebug @@ -47,8 +45,6 @@ be configured through `DebuggerRequires`. It has the same structure as do_something end -### Example: - # good def some_method diff --git a/config/contents/lint/deprecated_open_ssl_constant.md b/config/contents/lint/deprecated_open_ssl_constant.md index 96571a48..e6f7d7af 100644 --- a/config/contents/lint/deprecated_open_ssl_constant.md +++ b/config/contents/lint/deprecated_open_ssl_constant.md @@ -4,28 +4,18 @@ instead. ### Example: - # Example for OpenSSL::Cipher instantiation. - # bad OpenSSL::Cipher::AES.new(128, :GCM) # good OpenSSL::Cipher.new('aes-128-gcm') -### Example: - - # Example for OpenSSL::Digest instantiation. - # bad OpenSSL::Digest::SHA256.new # good OpenSSL::Digest.new('SHA256') -### Example: - - # Example for ::Digest inherited class methods. - # bad OpenSSL::Digest::SHA256.digest('foo') diff --git a/config/contents/lint/duplicate_branch.md b/config/contents/lint/duplicate_branch.md index 710c53d1..3afdc2ea 100644 --- a/config/contents/lint/duplicate_branch.md +++ b/config/contents/lint/duplicate_branch.md @@ -10,6 +10,9 @@ the above basic literal values. With `IgnoreConstantBranches: true`, branches are not registered as offenses if they return a constant value. +With `IgnoreDuplicateElseBranch: true`, in conditionals with multiple branches, +duplicate 'else' branches are not registered as offenses. + ### Example: # bad if foo @@ -77,3 +80,13 @@ as offenses if they return a constant value. when "large" then LARGE_SIZE else MEDIUM_SIZE end + +### Example: IgnoreDuplicateElseBranch: true + # good + if foo + do_foo + elsif bar + do_bar + else + do_foo + end diff --git a/config/contents/lint/duplicate_case_condition.md b/config/contents/lint/duplicate_case_condition.md index c636f1af..cfa99a6f 100644 --- a/config/contents/lint/duplicate_case_condition.md +++ b/config/contents/lint/duplicate_case_condition.md @@ -4,7 +4,6 @@ used in case 'when' expressions. ### Example: # bad - case x when 'first' do_something @@ -12,10 +11,7 @@ used in case 'when' expressions. do_something_else end -### Example: - # good - case x when 'first' do_something diff --git a/config/contents/lint/duplicate_hash_key.md b/config/contents/lint/duplicate_hash_key.md index 520de6d7..ca65d9fe 100644 --- a/config/contents/lint/duplicate_hash_key.md +++ b/config/contents/lint/duplicate_hash_key.md @@ -6,11 +6,7 @@ This cop mirrors a warning in Ruby 2.2. ### Example: # bad - hash = { food: 'apple', food: 'orange' } -### Example: - # good - hash = { food: 'apple', other_food: 'orange' } \ No newline at end of file diff --git a/config/contents/lint/duplicate_methods.md b/config/contents/lint/duplicate_methods.md index ddff9c78..5d91d9b7 100644 --- a/config/contents/lint/duplicate_methods.md +++ b/config/contents/lint/duplicate_methods.md @@ -4,7 +4,6 @@ definitions. ### Example: # bad - def foo 1 end @@ -13,20 +12,14 @@ definitions. 2 end -### Example: - # bad - def foo 1 end alias foo bar -### Example: - # good - def foo 1 end @@ -35,10 +28,7 @@ definitions. 2 end -### Example: - # good - def foo 1 end diff --git a/config/contents/lint/duplicate_regexp_character_class_element.md b/config/contents/lint/duplicate_regexp_character_class_element.md index 8036cd23..343c5e7a 100644 --- a/config/contents/lint/duplicate_regexp_character_class_element.md +++ b/config/contents/lint/duplicate_regexp_character_class_element.md @@ -1,4 +1,4 @@ -Checks for duplicate elements in Regexp character classes. +Checks for duplicate elements in `Regexp` character classes. ### Example: diff --git a/config/contents/lint/duplicate_set_element.md b/config/contents/lint/duplicate_set_element.md new file mode 100644 index 00000000..47e5887c --- /dev/null +++ b/config/contents/lint/duplicate_set_element.md @@ -0,0 +1,33 @@ +Checks for duplicate literal, constant, or variable elements in `Set` and `SortedSet`. + +### Example: + + # bad + Set[:foo, :bar, :foo] + + # good + Set[:foo, :bar] + + # bad + Set.new([:foo, :bar, :foo]) + + # good + Set.new([:foo, :bar]) + + # bad + [:foo, :bar, :foo].to_set + + # good + [:foo, :bar].to_set + + # bad + SortedSet[:foo, :bar, :foo] + + # good + SortedSet[:foo, :bar] + + # bad + SortedSet.new([:foo, :bar, :foo]) + + # good + SortedSet.new([:foo, :bar]) \ No newline at end of file diff --git a/config/contents/lint/each_with_object_argument.md b/config/contents/lint/each_with_object_argument.md index bbb1fd5d..d1f06f2d 100644 --- a/config/contents/lint/each_with_object_argument.md +++ b/config/contents/lint/each_with_object_argument.md @@ -7,12 +7,8 @@ It's definitely a bug. ### Example: # bad - sum = numbers.each_with_object(0) { |e, a| a += e } -### Example: - # good - num = 0 sum = numbers.each_with_object(num) { |e, a| a += e } \ No newline at end of file diff --git a/config/contents/lint/else_layout.md b/config/contents/lint/else_layout.md index c842bf77..79a4b72c 100644 --- a/config/contents/lint/else_layout.md +++ b/config/contents/lint/else_layout.md @@ -16,8 +16,6 @@ with `elsif` and `else`, you will have to correct it manually. do_that end -### Example: - # good # This code is compatible with the bad case. It will be autocorrected like this. diff --git a/config/contents/lint/empty_conditional_body.md b/config/contents/lint/empty_conditional_body.md index 63f23551..b54d78a4 100644 --- a/config/contents/lint/empty_conditional_body.md +++ b/config/contents/lint/empty_conditional_body.md @@ -2,12 +2,6 @@ Checks for the presence of `if`, `elsif` and `unless` branches without a body. NOTE: empty `else` branches are handled by `Style/EmptyElse`. -### Safety: - -Autocorrection for this cop is not safe. The conditions for empty branches that -the autocorrection removes may have side effects, or the logic in subsequent -branches may change due to the removal of a previous condition. - ### Example: # bad if condition @@ -33,6 +27,13 @@ branches may change due to the removal of a previous condition. do_something end + # good + if condition + do_something + elsif other_condition + nil + end + # good if condition do_something diff --git a/config/contents/lint/empty_ensure.md b/config/contents/lint/empty_ensure.md index 1876e431..81c42600 100644 --- a/config/contents/lint/empty_ensure.md +++ b/config/contents/lint/empty_ensure.md @@ -1,37 +1,27 @@ -Checks for empty `ensure` blocks +Checks for empty `ensure` blocks. ### Example: # bad - def some_method do_something ensure end -### Example: - # bad - begin do_something ensure end -### Example: - # good - def some_method do_something ensure do_something_else end -### Example: - # good - begin do_something ensure diff --git a/config/contents/lint/empty_expression.md b/config/contents/lint/empty_expression.md index e3e65bc1..434a3268 100644 --- a/config/contents/lint/empty_expression.md +++ b/config/contents/lint/empty_expression.md @@ -9,8 +9,6 @@ Checks for the presence of empty expressions. bar end -### Example: - # good foo = (some_expression) diff --git a/config/contents/lint/empty_interpolation.md b/config/contents/lint/empty_interpolation.md index 8535638f..fdc7d71d 100644 --- a/config/contents/lint/empty_interpolation.md +++ b/config/contents/lint/empty_interpolation.md @@ -3,11 +3,7 @@ Checks for empty interpolation. ### Example: # bad - "result is #{}" -### Example: - # good - "result is #{some_result}" \ No newline at end of file diff --git a/config/contents/lint/empty_when.md b/config/contents/lint/empty_when.md index b7ce9158..05a0c9b2 100644 --- a/config/contents/lint/empty_when.md +++ b/config/contents/lint/empty_when.md @@ -9,8 +9,6 @@ Checks for the presence of `when` branches without a body. when baz end -### Example: - # good case condition when foo diff --git a/config/contents/lint/ensure_return.md b/config/contents/lint/ensure_return.md index 47097ecb..7a8f49d8 100644 --- a/config/contents/lint/ensure_return.md +++ b/config/contents/lint/ensure_return.md @@ -8,7 +8,6 @@ If you want to rescue some (or all) exceptions, best to do it explicitly ### Example: # bad - def foo do_something ensure @@ -16,10 +15,7 @@ If you want to rescue some (or all) exceptions, best to do it explicitly return self end -### Example: - # good - def foo do_something self @@ -27,8 +23,7 @@ If you want to rescue some (or all) exceptions, best to do it explicitly cleanup end - # also good - + # good def foo begin do_something diff --git a/config/contents/lint/float_comparison.md b/config/contents/lint/float_comparison.md index aa8da4a9..c89036ce 100644 --- a/config/contents/lint/float_comparison.md +++ b/config/contents/lint/float_comparison.md @@ -13,7 +13,7 @@ if you perform any arithmetic operations involving precision loss. # good - using BigDecimal x.to_d == 0.1.to_d - # good - comparing against zero + # good - comparing against zero x == 0.0 x != 0.0 @@ -24,5 +24,8 @@ if you perform any arithmetic operations involving precision loss. tolerance = 0.0001 (x - 0.1).abs < tolerance + # good - comparing against nil + Float(x, exception: false) == nil + # Or some other epsilon based type of comparison: # https://www.embeddeduse.com/2019/08/26/qt-compare-two-floats/ diff --git a/config/contents/lint/float_out_of_range.md b/config/contents/lint/float_out_of_range.md index 93c35275..c1383e94 100644 --- a/config/contents/lint/float_out_of_range.md +++ b/config/contents/lint/float_out_of_range.md @@ -1,15 +1,11 @@ -Identifies Float literals which are, like, really really really +Identifies `Float` literals which are, like, really really really really really really really really big. Too big. No-one needs Floats that big. If you need a float that big, something is wrong with you. ### Example: # bad - float = 3.0e400 -### Example: - # good - float = 42.9 \ No newline at end of file diff --git a/config/contents/lint/format_parameter_mismatch.md b/config/contents/lint/format_parameter_mismatch.md index a42335cb..4363d79c 100644 --- a/config/contents/lint/format_parameter_mismatch.md +++ b/config/contents/lint/format_parameter_mismatch.md @@ -2,30 +2,20 @@ This lint sees if there is a mismatch between the number of expected fields for format/sprintf/#% and what is actually passed as arguments. -In addition it checks whether different formats are used in the same +In addition, it checks whether different formats are used in the same format string. Do not mix numbered, unnumbered, and named formats in the same format string. ### Example: # bad - format('A value: %s and another: %i', a_value) -### Example: - # good - format('A value: %s and another: %i', a_value, another) -### Example: - # bad - format('Unnumbered format: %s and numbered: %2$s', a_value, another) -### Example: - # good - format('Numbered format: %1$s and numbered %2$s', a_value, another) \ No newline at end of file diff --git a/config/contents/lint/hash_new_with_keyword_arguments_as_default.md b/config/contents/lint/hash_new_with_keyword_arguments_as_default.md new file mode 100644 index 00000000..c3d04f85 --- /dev/null +++ b/config/contents/lint/hash_new_with_keyword_arguments_as_default.md @@ -0,0 +1,21 @@ +Checks for the deprecated use of keyword arguments as a default in `Hash.new`. + +This usage raises a warning in Ruby 3.3 and results in an error in Ruby 3.4. +In Ruby 3.4, keyword arguments will instead be used to change the behavior of a hash. +For example, the capacity option can be passed to create a hash with a certain size +if you know it in advance, for better performance. + +NOTE: The following corner case may result in a false negative when upgrading from Ruby 3.3 +or earlier, but it is intentionally not detected to respect the expected usage in Ruby 3.4. + +```ruby +Hash.new(capacity: 42) +``` + +### Example: + + # bad + Hash.new(key: :value) + + # good + Hash.new({key: :value}) diff --git a/config/contents/lint/implicit_string_concatenation.md b/config/contents/lint/implicit_string_concatenation.md index 0db7d3f7..07a020ae 100644 --- a/config/contents/lint/implicit_string_concatenation.md +++ b/config/contents/lint/implicit_string_concatenation.md @@ -4,13 +4,9 @@ which are on the same line. ### Example: # bad - array = ['Item 1' 'Item 2'] -### Example: - # good - array = ['Item 1Item 2'] array = ['Item 1' + 'Item 2'] array = [ diff --git a/config/contents/lint/ineffective_access_modifier.md b/config/contents/lint/ineffective_access_modifier.md index b04033c1..659b5374 100644 --- a/config/contents/lint/ineffective_access_modifier.md +++ b/config/contents/lint/ineffective_access_modifier.md @@ -6,7 +6,6 @@ used for that. ### Example: # bad - class C private @@ -15,10 +14,7 @@ used for that. end end -### Example: - # good - class C def self.method puts 'hi' @@ -27,10 +23,7 @@ used for that. private_class_method :method end -### Example: - # good - class C class << self private diff --git a/config/contents/lint/interpolation_check.md b/config/contents/lint/interpolation_check.md index a07ddfaf..3629c4e7 100644 --- a/config/contents/lint/interpolation_check.md +++ b/config/contents/lint/interpolation_check.md @@ -10,11 +10,7 @@ the expression `foo`. ### Example: # bad - foo = 'something with #{interpolation} inside' -### Example: - # good - foo = "something with #{interpolation} inside" \ No newline at end of file diff --git a/config/contents/lint/literal_as_condition.md b/config/contents/lint/literal_as_condition.md index 3d22c1d6..e49d4209 100644 --- a/config/contents/lint/literal_as_condition.md +++ b/config/contents/lint/literal_as_condition.md @@ -13,12 +13,15 @@ NOTE: Literals in `case-in` condition where the match variable is used in end # bad - if some_var && true + # We're only interested in the left hand side being a truthy literal, + # because it affects the evaluation of the &&, whereas the right hand + # side will be conditionally executed/called and can be a literal. + if true && some_var do_something end # good - if some_var && some_condition + if some_var do_something end diff --git a/config/contents/lint/literal_in_interpolation.md b/config/contents/lint/literal_in_interpolation.md index 54395a23..e2684cb5 100644 --- a/config/contents/lint/literal_in_interpolation.md +++ b/config/contents/lint/literal_in_interpolation.md @@ -1,13 +1,12 @@ Checks for interpolated literals. +NOTE: Array literals interpolated in regexps are not handled by this cop, but +by `Lint/ArrayLiteralInRegexp` instead. + ### Example: # bad - "result is #{10}" -### Example: - # good - "result is 10" \ No newline at end of file diff --git a/config/contents/lint/loop.md b/config/contents/lint/loop.md index 3fb5b9b1..ec67db34 100644 --- a/config/contents/lint/loop.md +++ b/config/contents/lint/loop.md @@ -15,17 +15,6 @@ loop body raises a `StopIteration` exception (which `Kernel#loop` rescues). do_something end while some_condition -### Example: - - # bad - - # using until - begin - do_something - end until some_condition - -### Example: - # good # while replacement @@ -34,7 +23,12 @@ loop body raises a `StopIteration` exception (which `Kernel#loop` rescues). break unless some_condition end -### Example: + # bad + + # using until + begin + do_something + end until some_condition # good diff --git a/config/contents/lint/mixed_case_range.md b/config/contents/lint/mixed_case_range.md index c30d53bf..5af92d2e 100644 --- a/config/contents/lint/mixed_case_range.md +++ b/config/contents/lint/mixed_case_range.md @@ -3,7 +3,7 @@ Checks for mixed-case character ranges since they include likely unintended char Offenses are registered for regexp character classes like `/[A-z]/` as well as range objects like `('A'..'z')`. -NOTE: Range objects cannot be autocorrected. +NOTE: `Range` objects cannot be autocorrected. ### Safety: diff --git a/config/contents/lint/mixed_regexp_capture_types.md b/config/contents/lint/mixed_regexp_capture_types.md index 6e074a51..4321c11c 100644 --- a/config/contents/lint/mixed_regexp_capture_types.md +++ b/config/contents/lint/mixed_regexp_capture_types.md @@ -1,4 +1,4 @@ -Do not mix named captures and numbered captures in a Regexp literal +Do not mix named captures and numbered captures in a `Regexp` literal because numbered capture is ignored if they're mixed. Replace numbered captures with non-capturing groupings or named captures. diff --git a/config/contents/lint/nested_method_definition.md b/config/contents/lint/nested_method_definition.md index e69ae3de..c742139c 100644 --- a/config/contents/lint/nested_method_definition.md +++ b/config/contents/lint/nested_method_definition.md @@ -12,8 +12,6 @@ Checks for nested method definitions. end end -### Example: - # good def foo @@ -21,8 +19,6 @@ Checks for nested method definitions. bar.call end -### Example: - # good # `class_eval`, `instance_eval`, `module_eval`, `class_exec`, `instance_exec`, and @@ -42,8 +38,6 @@ Checks for nested method definitions. end end -### Example: - # good def foo diff --git a/config/contents/lint/next_without_accumulator.md b/config/contents/lint/next_without_accumulator.md index b3226b11..725007e9 100644 --- a/config/contents/lint/next_without_accumulator.md +++ b/config/contents/lint/next_without_accumulator.md @@ -3,16 +3,12 @@ Don't omit the accumulator when calling `next` in a `reduce` block. ### Example: # bad - result = (1..4).reduce(0) do |acc, i| next if i.odd? acc + i end -### Example: - # good - result = (1..4).reduce(0) do |acc, i| next acc if i.odd? acc + i diff --git a/config/contents/lint/no_return_in_begin_end_blocks.md b/config/contents/lint/no_return_in_begin_end_blocks.md index 40bc70cc..7bb08b03 100644 --- a/config/contents/lint/no_return_in_begin_end_blocks.md +++ b/config/contents/lint/no_return_in_begin_end_blocks.md @@ -6,17 +6,13 @@ method, possibly leading to unexpected behavior. ### Example: # bad - @some_variable ||= begin return some_value if some_condition_is_met do_something end -### Example: - # good - @some_variable ||= begin if some_condition_is_met some_value @@ -26,7 +22,6 @@ method, possibly leading to unexpected behavior. end # good - some_variable = if some_condition_is_met return if another_condition_is_met diff --git a/config/contents/lint/numeric_operation_with_constant_result.md b/config/contents/lint/numeric_operation_with_constant_result.md new file mode 100644 index 00000000..05c74801 --- /dev/null +++ b/config/contents/lint/numeric_operation_with_constant_result.md @@ -0,0 +1,39 @@ +Certain numeric operations have a constant result, usually 0 or 1. +Multiplying a number by 0 will always return 0. +Dividing a number by itself or raising it to the power of 0 will always return 1. +As such, they can be replaced with that result. +These are probably leftover from debugging, or are mistakes. +Other numeric operations that are similarly leftover from debugging or mistakes +are handled by `Lint/UselessNumericOperation`. + +NOTE: This cop doesn't detect offenses for the `-` and `%` operator because it +can't determine the type of `x`. If `x` is an `Array` or `String`, it doesn't perform +a numeric operation. + +### Example: + + # bad + x * 0 + + # good + 0 + + # bad + x *= 0 + + # good + x = 0 + + # bad + x / x + x ** 0 + + # good + 1 + + # bad + x /= x + x **= 0 + + # good + x = 1 diff --git a/config/contents/lint/out_of_range_regexp_ref.md b/config/contents/lint/out_of_range_regexp_ref.md index 7c282ab7..892ac429 100644 --- a/config/contents/lint/out_of_range_regexp_ref.md +++ b/config/contents/lint/out_of_range_regexp_ref.md @@ -1,4 +1,4 @@ -Looks for references of Regexp captures that are out of range +Looks for references of `Regexp` captures that are out of range and thus always returns nil. ### Safety: diff --git a/config/contents/lint/percent_string_array.md b/config/contents/lint/percent_string_array.md index 5e959679..5bef2e6c 100644 --- a/config/contents/lint/percent_string_array.md +++ b/config/contents/lint/percent_string_array.md @@ -17,11 +17,7 @@ and that might have been done purposely. ### Example: # bad - %w('foo', "bar") -### Example: - # good - %w(foo bar) \ No newline at end of file diff --git a/config/contents/lint/percent_symbol_array.md b/config/contents/lint/percent_symbol_array.md index 2177bd37..fa981b70 100644 --- a/config/contents/lint/percent_symbol_array.md +++ b/config/contents/lint/percent_symbol_array.md @@ -7,11 +7,7 @@ rather than meant to be part of the resulting symbols. ### Example: # bad - %i(:foo, :bar) -### Example: - # good - %i(foo bar) \ No newline at end of file diff --git a/config/contents/lint/raise_exception.md b/config/contents/lint/raise_exception.md index 4b34b56c..e6f09209 100644 --- a/config/contents/lint/raise_exception.md +++ b/config/contents/lint/raise_exception.md @@ -1,12 +1,15 @@ -Checks for `raise` or `fail` statements which are -raising `Exception` class. +Checks for `raise` or `fail` statements which raise `Exception` or +`Exception.new`. Use `StandardError` or a specific exception class instead. -You can specify a module name that will be an implicit namespace -using `AllowedImplicitNamespaces` option. The cop cause a false positive -for namespaced `Exception` when a namespace is omitted. This option can -prevent the false positive by specifying a namespace to be omitted for -`Exception`. Alternatively, make `Exception` a fully qualified class -name with an explicit namespace. +If you have defined your own namespaced `Exception` class, it is possible +to configure the cop to allow it by setting `AllowedImplicitNamespaces` to +an array with the names of the namespaces to allow. By default, this is set to +`['Gem']`, which allows `Gem::Exception` to be raised without an explicit namespace. +If not allowed, a false positive may be registered if `raise Exception` is called +within the namespace. + +Alternatively, use a fully qualified name with `raise`/`fail` +(eg. `raise Namespace::Exception`). ### Safety: @@ -16,14 +19,30 @@ raised, which is a change in behavior. ### Example: # bad raise Exception, 'Error message here' + raise Exception.new('Error message here') # good raise StandardError, 'Error message here' + raise MyError.new, 'Error message here' + +### Example: AllowedImplicitNamespaces: ['Gem'] (default) + # bad - `Foo` is not an allowed implicit namespace + module Foo + def self.foo + raise Exception # This is qualified to `Foo::Exception`. + end + end -### Example: AllowedImplicitNamespaces: ['Gem'] # good module Gem def self.foo - raise Exception # This exception means `Gem::Exception`. + raise Exception # This is qualified to `Gem::Exception`. + end + end + + # good + module Foo + def self.foo + raise Foo::Exception end end \ No newline at end of file diff --git a/config/contents/lint/rand_one.md b/config/contents/lint/rand_one.md index b729d9d9..223620ad 100644 --- a/config/contents/lint/rand_one.md +++ b/config/contents/lint/rand_one.md @@ -4,14 +4,10 @@ Such calls always return `0`. ### Example: # bad - rand 1 Kernel.rand(-1) rand 1.0 rand(-1.0) -### Example: - # good - 0 # just use 0 instead \ No newline at end of file diff --git a/config/contents/lint/redundant_cop_enable_directive.md b/config/contents/lint/redundant_cop_enable_directive.md index 875b52e4..fd6646d0 100644 --- a/config/contents/lint/redundant_cop_enable_directive.md +++ b/config/contents/lint/redundant_cop_enable_directive.md @@ -3,14 +3,16 @@ removed. When comment enables all cops at once `rubocop:enable all` that cop checks whether any cop was actually enabled. + ### Example: + # bad foo = 1 # rubocop:enable Layout/LineLength # good foo = 1 -### Example: + # bad # rubocop:disable Style/StringLiterals foo = "1" diff --git a/config/contents/lint/redundant_regexp_quantifiers.md b/config/contents/lint/redundant_regexp_quantifiers.md index bdf97594..67d03760 100644 --- a/config/contents/lint/redundant_regexp_quantifiers.md +++ b/config/contents/lint/redundant_regexp_quantifiers.md @@ -1,4 +1,4 @@ -Checks for redundant quantifiers inside Regexp literals. +Checks for redundant quantifiers inside `Regexp` literals. It is always allowed when interpolation is used in a regexp literal, because it's unknown what kind of string will be expanded as a result: diff --git a/config/contents/lint/redundant_require_statement.md b/config/contents/lint/redundant_require_statement.md index edbb9db7..bb2c088f 100644 --- a/config/contents/lint/redundant_require_statement.md +++ b/config/contents/lint/redundant_require_statement.md @@ -12,18 +12,12 @@ Below are the features that each `TargetRubyVersion` targets. * 2.0+ ... `enumerator` * 2.1+ ... `thread` * 2.2+ ... Add `rational` and `complex` above - * 2.5+ ... Add `pp` above * 2.7+ ... Add `ruby2_keywords` above * 3.1+ ... Add `fiber` above * 3.2+ ... `set` This cop target those features. -### Safety: - -This cop's autocorrection is unsafe because if `require 'pp'` is removed from one file, -`NameError` can be encountered when another file uses `PP.pp`. - ### Example: # bad require 'unloaded_feature' diff --git a/config/contents/lint/redundant_safe_navigation.md b/config/contents/lint/redundant_safe_navigation.md index e6f152ee..f957a37c 100644 --- a/config/contents/lint/redundant_safe_navigation.md +++ b/config/contents/lint/redundant_safe_navigation.md @@ -1,7 +1,7 @@ Checks for redundant safe navigation calls. Use cases where a constant, named in camel case for classes and modules is `nil` are rare, and an offense is not detected when the receiver is a constant. The detection also applies -to literal receivers, except for `nil`. +to `self`, and to literal receivers, except for `nil`. For all receivers, the `instance_of?`, `kind_of?`, `is_a?`, `eql?`, `respond_to?`, and `equal?` methods are checked by default. @@ -25,6 +25,9 @@ will be autocorrected to never return `nil`. # bad CamelCaseConst&.do_something + # good + CamelCaseConst.do_something + # bad do_something if attrs&.respond_to?(:[]) @@ -36,9 +39,6 @@ will be autocorrected to never return `nil`. node = node.parent end - # good - CamelCaseConst.do_something - # good while node.is_a?(BeginNode) node = node.parent @@ -63,6 +63,12 @@ will be autocorrected to never return `nil`. foo.to_f foo.to_s + # bad + self&.foo + + # good + self.foo + ### Example: AllowedMethods: [nil_safe_method] # bad do_something if attrs&.nil_safe_method(:[]) diff --git a/config/contents/lint/redundant_splat_expansion.md b/config/contents/lint/redundant_splat_expansion.md index 5a610991..c6e043f4 100644 --- a/config/contents/lint/redundant_splat_expansion.md +++ b/config/contents/lint/redundant_splat_expansion.md @@ -1,4 +1,4 @@ -Checks for unneeded usages of splat expansion +Checks for unneeded usages of splat expansion. ### Example: diff --git a/config/contents/lint/redundant_string_coercion.md b/config/contents/lint/redundant_string_coercion.md index 793ab5ea..afa1b33b 100644 --- a/config/contents/lint/redundant_string_coercion.md +++ b/config/contents/lint/redundant_string_coercion.md @@ -4,16 +4,12 @@ which is redundant. ### Example: # bad - "result is #{something.to_s}" print something.to_s puts something.to_s warn something.to_s -### Example: - # good - "result is #{something}" print something puts something diff --git a/config/contents/lint/redundant_type_conversion.md b/config/contents/lint/redundant_type_conversion.md new file mode 100644 index 00000000..ff477194 --- /dev/null +++ b/config/contents/lint/redundant_type_conversion.md @@ -0,0 +1,73 @@ +Checks for redundant uses of `to_s`, `to_sym`, `to_i`, `to_f`, `to_d`, `to_r`, `to_c`, +`to_a`, `to_h`, and `to_set`. + +When one of these methods is called on an object of the same type, that object +is returned, making the call unnecessary. The cop detects conversion methods called +on object literals, class constructors, class `[]` methods, and the `Kernel` methods +`String()`, `Integer()`, `Float()`, BigDecimal(), `Rational()`, `Complex()`, and `Array()`. + +Specifically, these cases are detected for each conversion method: + +* `to_s` when called on a string literal, interpolated string, heredoc, + or with `String.new` or `String()`. +* `to_sym` when called on a symbol literal or interpolated symbol. +* `to_i` when called on an integer literal or with `Integer()`. +* `to_f` when called on a float literal of with `Float()`. +* `to_r` when called on a rational literal or with `Rational()`. +* `to_c` when called on a complex literal of with `Complex()`. +* `to_a` when called on an array literal, or with `Array.new`, `Array()` or `Array[]`. +* `to_h` when called on a hash literal, or with `Hash.new`, `Hash()` or `Hash[]`. +* `to_set` when called on `Set.new` or `Set[]`. + +In all cases, chaining one same `to_*` conversion methods listed above is redundant. + +The cop can also register an offense for chaining conversion methods on methods that are +expected to return a specific type regardless of receiver (eg. `foo.inspect.to_s` and +`foo.to_json.to_s`). + +### Example: + # bad + "text".to_s + :sym.to_sym + 42.to_i + 8.5.to_f + 12r.to_r + 1i.to_c + [].to_a + {}.to_h + Set.new.to_set + + # good + "text" + :sym + 42 + 8.5 + 12r + 1i + [] + {} + Set.new + + # bad + Integer(var).to_i + + # good + Integer(var) + + # good - chaining to a type constructor with exceptions suppressed + # in this case, `Integer()` could return `nil` + Integer(var, exception: false).to_i + + # bad - chaining the same conversion + foo.to_s.to_s + + # good + foo.to_s + + # bad - chaining a conversion to a method that is expected to return the same type + foo.inspect.to_s + foo.to_json.to_s + + # good + foo.inspect + foo.to_json diff --git a/config/contents/lint/refinement_import_methods.md b/config/contents/lint/refinement_import_methods.md index 6703ca5e..1425f774 100644 --- a/config/contents/lint/refinement_import_methods.md +++ b/config/contents/lint/refinement_import_methods.md @@ -1,7 +1,7 @@ Checks if `include` or `prepend` is called in `refine` block. These methods are deprecated and should be replaced with `Refinement#import_methods`. -It emulates deprecation warnings in Ruby 3.1. +It emulates deprecation warnings in Ruby 3.1. Functionality has been removed in Ruby 3.2. ### Safety: diff --git a/config/contents/lint/require_parentheses.md b/config/contents/lint/require_parentheses.md index bf196364..484fa843 100644 --- a/config/contents/lint/require_parentheses.md +++ b/config/contents/lint/require_parentheses.md @@ -10,15 +10,11 @@ an operand of &&/||. ### Example: # bad - if day.is? :tuesday && month == :jan # ... end -### Example: - # good - if day.is?(:tuesday) && month == :jan # ... end \ No newline at end of file diff --git a/config/contents/lint/rescue_exception.md b/config/contents/lint/rescue_exception.md index ec975aff..30298e86 100644 --- a/config/contents/lint/rescue_exception.md +++ b/config/contents/lint/rescue_exception.md @@ -1,19 +1,15 @@ -Checks for `rescue` blocks targeting the Exception class. +Checks for `rescue` blocks targeting the `Exception` class. ### Example: # bad - begin do_something rescue Exception handle_exception end -### Example: - # good - begin do_something rescue ArgumentError diff --git a/config/contents/lint/return_in_void_context.md b/config/contents/lint/return_in_void_context.md index eb9fd809..5df54c2d 100644 --- a/config/contents/lint/return_in_void_context.md +++ b/config/contents/lint/return_in_void_context.md @@ -14,8 +14,6 @@ where the value will be ignored. (initialize and setter methods) return 42 end -### Example: - # good def initialize foo diff --git a/config/contents/lint/safe_navigation_chain.md b/config/contents/lint/safe_navigation_chain.md index 6feb81d2..906f1b82 100644 --- a/config/contents/lint/safe_navigation_chain.md +++ b/config/contents/lint/safe_navigation_chain.md @@ -7,14 +7,10 @@ This cop checks for the problem outlined above. ### Example: # bad - x&.foo.bar x&.foo + bar x&.foo[bar] -### Example: - # good - x&.foo&.bar x&.foo || bar \ No newline at end of file diff --git a/config/contents/lint/safe_navigation_consistency.md b/config/contents/lint/safe_navigation_consistency.md index 7d5e51bd..a7e2f331 100644 --- a/config/contents/lint/safe_navigation_consistency.md +++ b/config/contents/lint/safe_navigation_consistency.md @@ -1,22 +1,34 @@ -Check to make sure that if safe navigation is used for a method -call in an `&&` or `||` condition that safe navigation is used for all -method calls on that same object. +Check to make sure that if safe navigation is used in an `&&` or `||` condition, +consistent and appropriate safe navigation, without excess or deficiency, +is used for all method calls on the same object. ### Example: # bad - foo&.bar && foo.baz + foo&.bar && foo&.baz - # bad - foo.bar || foo&.baz + # good + foo&.bar && foo.baz # bad - foo&.bar && (foobar.baz || foo.baz) + foo.bar && foo&.baz # good foo.bar && foo.baz + # bad + foo&.bar || foo.baz + # good foo&.bar || foo&.baz + # bad + foo.bar || foo&.baz + # good + foo.bar || foo.baz + + # bad foo&.bar && (foobar.baz || foo&.baz) + + # good + foo&.bar && (foobar.baz || foo.baz) diff --git a/config/contents/lint/shadowing_outer_local_variable.md b/config/contents/lint/shadowing_outer_local_variable.md index 04353df3..6c966e1c 100644 --- a/config/contents/lint/shadowing_outer_local_variable.md +++ b/config/contents/lint/shadowing_outer_local_variable.md @@ -7,16 +7,15 @@ NOTE: Shadowing of variables in block passed to `Ractor.new` is allowed because `Ractor` should not access outer variables. eg. following style is encouraged: - ```ruby - worker_id, pipe = env - Ractor.new(worker_id, pipe) do |worker_id, pipe| - end - ``` +```ruby +worker_id, pipe = env +Ractor.new(worker_id, pipe) do |worker_id, pipe| +end +``` ### Example: # bad - def some_method foo = 1 @@ -25,10 +24,7 @@ eg. following style is encouraged: end end -### Example: - # good - def some_method foo = 1 diff --git a/config/contents/lint/shared_mutable_default.md b/config/contents/lint/shared_mutable_default.md new file mode 100644 index 00000000..58f67a2b --- /dev/null +++ b/config/contents/lint/shared_mutable_default.md @@ -0,0 +1,41 @@ +Checks for `Hash` creation with a mutable default value. +Creating a `Hash` in such a way will share the default value +across all keys, causing unexpected behavior when modifying it. + +For example, when the `Hash` was created with an `Array` as the argument, +calling `hash[:foo] << 'bar'` will also change the value of all +other keys that have not been explicitly assigned to. + +### Example: + # bad + Hash.new([]) + Hash.new({}) + Hash.new(Array.new) + Hash.new(Hash.new) + + # okay -- In rare cases that intentionally have this behavior, + # without disabling the cop, you can set the default explicitly. + h = Hash.new + h.default = [] + h[:a] << 1 + h[:b] << 2 + h # => {:a => [1, 2], :b => [1, 2]} + + # okay -- beware this will discard mutations and only remember assignments + Hash.new { Array.new } + Hash.new { Hash.new } + Hash.new { {} } + Hash.new { [] } + + # good - frozen solution will raise an error when mutation attempted + Hash.new([].freeze) + Hash.new({}.freeze) + + # good - using a proc will create a new object for each key + h = Hash.new + h.default_proc = ->(h, k) { [] } + h.default_proc = ->(h, k) { {} } + + # good - using a block will create a new object for each key + Hash.new { |h, k| h[k] = [] } + Hash.new { |h, k| h[k] = {} } \ No newline at end of file diff --git a/config/contents/lint/suppressed_exception_in_number_conversion.md b/config/contents/lint/suppressed_exception_in_number_conversion.md new file mode 100644 index 00000000..b4ba338a --- /dev/null +++ b/config/contents/lint/suppressed_exception_in_number_conversion.md @@ -0,0 +1,34 @@ +Checks for cases where exceptions unrelated to the numeric constructors `Integer()`, +`Float()`, `BigDecimal()`, `Complex()`, and `Rational()` may be unintentionally swallowed. + +### Safety: + +The cop is unsafe for autocorrection because unexpected errors occurring in the argument +passed to numeric constructor (e.g., `Integer()`) can lead to incompatible behavior. +For example, changing it to `Integer(potential_exception_method_call, exception: false)` +ensures that exceptions raised by `potential_exception_method_call` are not ignored. + +```ruby +Integer(potential_exception_method_call) rescue nil +``` + +### Example: + + # bad + Integer(arg) rescue nil + + # bad + begin + Integer(arg) + rescue + nil + end + + # bad + begin + Integer(arg) + rescue + end + + # good + Integer(arg, exception: false) diff --git a/config/contents/lint/unescaped_bracket_in_regexp.md b/config/contents/lint/unescaped_bracket_in_regexp.md new file mode 100644 index 00000000..04b6be13 --- /dev/null +++ b/config/contents/lint/unescaped_bracket_in_regexp.md @@ -0,0 +1,22 @@ +Checks for Regexpes (both literals and via `Regexp.new` / `Regexp.compile`) +that contain unescaped `]` characters. + +It emulates the following Ruby warning: + +```ruby +$ ruby -e '/abc]123/' +-e:1: warning: regular expression has ']' without escape: /abc]123/ +``` + +### Example: + # bad + /abc]123/ + %r{abc]123} + Regexp.new('abc]123') + Regexp.compile('abc]123') + + # good + /abc\]123/ + %r{abc\]123} + Regexp.new('abc\]123') + Regexp.compile('abc\]123') diff --git a/config/contents/lint/unified_integer.md b/config/contents/lint/unified_integer.md index 71005cd9..959bcacf 100644 --- a/config/contents/lint/unified_integer.md +++ b/config/contents/lint/unified_integer.md @@ -3,12 +3,8 @@ Checks for using Fixnum or Bignum constant. ### Example: # bad - 1.is_a?(Fixnum) 1.is_a?(Bignum) -### Example: - # good - 1.is_a?(Integer) \ No newline at end of file diff --git a/config/contents/lint/unreachable_code.md b/config/contents/lint/unreachable_code.md index 3af20b6b..182cb0fd 100644 --- a/config/contents/lint/unreachable_code.md +++ b/config/contents/lint/unreachable_code.md @@ -5,14 +5,12 @@ statement in non-final position in `begin` (implicit) blocks. ### Example: # bad - def some_method return do_something end # bad - def some_method if cond return @@ -22,10 +20,7 @@ statement in non-final position in `begin` (implicit) blocks. do_something end -### Example: - # good - def some_method do_something end \ No newline at end of file diff --git a/config/contents/lint/unused_method_argument.md b/config/contents/lint/unused_method_argument.md index 473f2ca3..0632e26c 100644 --- a/config/contents/lint/unused_method_argument.md +++ b/config/contents/lint/unused_method_argument.md @@ -34,6 +34,8 @@ Checks for unused method arguments. end ### Example: IgnoreNotImplementedMethods: true (default) + # with default value of `NotImplementedExceptions: ['NotImplementedError']` + # good def do_something(unused) raise NotImplementedError @@ -43,6 +45,14 @@ Checks for unused method arguments. fail "TODO" end +### Example: IgnoreNotImplementedMethods: true + # with `NotImplementedExceptions: ['AbstractMethodError']` + + # good + def do_something(unused) + raise AbstractMethodError + end + ### Example: IgnoreNotImplementedMethods: false # bad def do_something(unused) @@ -51,4 +61,4 @@ Checks for unused method arguments. def do_something_else(unused) fail "TODO" - end + end \ No newline at end of file diff --git a/config/contents/lint/uri_regexp.md b/config/contents/lint/uri_regexp.md index dfd29c3b..318b7c44 100644 --- a/config/contents/lint/uri_regexp.md +++ b/config/contents/lint/uri_regexp.md @@ -1,9 +1,21 @@ -Identifies places where `URI.regexp` is obsolete and should -not be used. Instead, use `URI::DEFAULT_PARSER.make_regexp`. +Identifies places where `URI.regexp` is obsolete and should not be used. + +For Ruby 3.3 or lower, use `URI::DEFAULT_PARSER.make_regexp`. +For Ruby 3.4 or higher, use `URI::RFC2396_PARSER.make_regexp`. + +NOTE: If you need to support both Ruby 3.3 and lower as well as Ruby 3.4 and higher, +consider manually changing the code as follows: + +```ruby +defined?(URI::RFC2396_PARSER) ? URI::RFC2396_PARSER : URI::DEFAULT_PARSER +``` ### Example: # bad URI.regexp('http://example.com') - # good + # good - Ruby 3.3 or lower URI::DEFAULT_PARSER.make_regexp('http://example.com') + + # good - Ruby 3.4 or higher + URI::RFC2396_PARSER.make_regexp('http://example.com') diff --git a/config/contents/lint/useless_assignment.md b/config/contents/lint/useless_assignment.md index 717e4099..bb3b13d1 100644 --- a/config/contents/lint/useless_assignment.md +++ b/config/contents/lint/useless_assignment.md @@ -10,29 +10,23 @@ Currently this cop has advanced logic that detects unreferenced reassignments and properly handles varied cases such as branch, loop, rescue, ensure, etc. +This cop's autocorrection avoids cases like `a ||= 1` because removing assignment from +operator assignment can cause `NameError` if this assignment has been used to declare +a local variable. For example, replacing `a ||= 1` with `a || 1` may cause +"undefined local variable or method `a' for main:Object (NameError)". + NOTE: Given the assignment `foo = 1, bar = 2`, removing unused variables can lead to a syntax error, so this case is not autocorrected. -### Safety: - -This cop's autocorrection is unsafe because removing assignment from -operator assignment can cause NameError if this assignment has been used to declare -local variable. For example, replacing `a ||= 1` to `a || 1` may cause -"undefined local variable or method `a' for main:Object (NameError)". - ### Example: # bad - def some_method some_var = 1 do_something end -### Example: - # good - def some_method some_var = 1 do_something(some_var) diff --git a/config/contents/lint/useless_constant_scoping.md b/config/contents/lint/useless_constant_scoping.md new file mode 100644 index 00000000..7c1fa86e --- /dev/null +++ b/config/contents/lint/useless_constant_scoping.md @@ -0,0 +1,25 @@ +Checks for useless constant scoping. Private constants must be defined using +`private_constant`. Even if `private` access modifier is used, it is public scope despite +its appearance. + +It does not support autocorrection due to behavior change and multiple ways to fix it. +Or a public constant may be intended. + +### Example: + + # bad + class Foo + private + PRIVATE_CONST = 42 + end + + # good + class Foo + PRIVATE_CONST = 42 + private_constant :PRIVATE_CONST + end + + # good + class Foo + PUBLIC_CONST = 42 # If private scope is not intended. + end diff --git a/config/contents/lint/useless_defined.md b/config/contents/lint/useless_defined.md new file mode 100644 index 00000000..9f8eadca --- /dev/null +++ b/config/contents/lint/useless_defined.md @@ -0,0 +1,36 @@ +Checks for calls to `defined?` with strings or symbols as the argument. +Such calls will always return `'expression'`, you probably meant to +check for the existence of a constant, method, or variable instead. + +`defined?` is part of the Ruby syntax and doesn't behave like normal methods. +You can safely pass in what you are checking for directly, without encountering +a `NameError`. + +When interpolation is used, oftentimes it is not possible to write the +code with `defined?`. In these cases, switch to one of the more specific methods: + +* `class_variable_defined?` +* `const_defined?` +* `method_defined?` +* `instance_variable_defined?` +* `binding.local_variable_defined?` + +### Example: + + # bad + defined?('FooBar') + defined?(:FooBar) + defined?(:foo_bar) + defined?('foo_bar') + + # good + defined?(FooBar) + defined?(foo_bar) + + # bad - interpolation + bar = 'Bar' + defined?("Foo::#{bar}::Baz") + + # good + bar = 'Bar' + defined?(Foo) && Foo.const_defined?(bar) && Foo.const_get(bar).const_defined?(:Baz) \ No newline at end of file diff --git a/config/contents/lint/useless_else_without_rescue.md b/config/contents/lint/useless_else_without_rescue.md index 12fdf021..9b63a28d 100644 --- a/config/contents/lint/useless_else_without_rescue.md +++ b/config/contents/lint/useless_else_without_rescue.md @@ -5,17 +5,13 @@ NOTE: This syntax is no longer valid on Ruby 2.6 or higher. ### Example: # bad - begin do_something else do_something_else # This will never be run. end -### Example: - # good - begin do_something rescue diff --git a/config/contents/lint/useless_numeric_operation.md b/config/contents/lint/useless_numeric_operation.md new file mode 100644 index 00000000..ae33c163 --- /dev/null +++ b/config/contents/lint/useless_numeric_operation.md @@ -0,0 +1,25 @@ +Certain numeric operations have no impact, being: +Adding or subtracting 0, multiplying or dividing by 1 or raising to the power of 1. +These are probably leftover from debugging, or are mistakes. + +### Example: + + # bad + x + 0 + x - 0 + x * 1 + x / 1 + x ** 1 + + # good + x + + # bad + x += 0 + x -= 0 + x *= 1 + x /= 1 + x **= 1 + + # good + x = x diff --git a/config/contents/lint/useless_setter_call.md b/config/contents/lint/useless_setter_call.md index f4a7d328..15191037 100644 --- a/config/contents/lint/useless_setter_call.md +++ b/config/contents/lint/useless_setter_call.md @@ -13,16 +13,12 @@ return value will be changed. ### Example: # bad - def something x = Something.new x.attr = 5 end -### Example: - # good - def something x = Something.new x.attr = 5 diff --git a/config/contents/metrics/block_length.md b/config/contents/metrics/block_length.md index 8f18efe3..1098e051 100644 --- a/config/contents/metrics/block_length.md +++ b/config/contents/metrics/block_length.md @@ -4,8 +4,9 @@ The maximum allowed length is configurable. The cop can be configured to ignore blocks passed to certain methods. You can set constructs you want to fold with `CountAsOne`. -Available are: 'array', 'hash', 'heredoc', and 'method_call'. Each construct -will be counted as one line regardless of its actual size. + +Available are: 'array', 'hash', 'heredoc', and 'method_call'. +Each construct will be counted as one line regardless of its actual size. NOTE: This cop does not apply for `Struct` definitions. @@ -13,7 +14,7 @@ NOTE: The `ExcludedMethods` configuration is deprecated and only kept for backwards compatibility. Please use `AllowedMethods` and `AllowedPatterns` instead. By default, there are no methods to allowed. -### Example: CountAsOne: ['array', 'heredoc', 'method_call'] +### Example: CountAsOne: ['array', 'hash', 'heredoc', 'method_call'] something do array = [ # +1 @@ -21,7 +22,7 @@ instead. By default, there are no methods to allowed. 2 ] - hash = { # +3 + hash = { # +1 key: 'value' } @@ -34,4 +35,4 @@ instead. By default, there are no methods to allowed. 1, 2 ) - end # 6 points + end # 4 points diff --git a/config/contents/metrics/block_nesting.md b/config/contents/metrics/block_nesting.md index 413c93b4..08282195 100644 --- a/config/contents/metrics/block_nesting.md +++ b/config/contents/metrics/block_nesting.md @@ -1,8 +1,8 @@ -Checks for excessive nesting of conditional and looping -constructs. +Checks for excessive nesting of conditional and looping constructs. -You can configure if blocks are considered using the `CountBlocks` -option. When set to `false` (the default) blocks are not counted -towards the nesting level. Set to `true` to count blocks as well. +You can configure if blocks are considered using the `CountBlocks` and `CountModifierForms` +options. When both are set to `false` (the default) blocks and modifier forms are not +counted towards the nesting level. Set them to `true` to include these in the nesting level +calculation as well. The maximum level of nesting allowed is configurable. \ No newline at end of file diff --git a/config/contents/metrics/class_length.md b/config/contents/metrics/class_length.md index bb2823b0..e91bb50d 100644 --- a/config/contents/metrics/class_length.md +++ b/config/contents/metrics/class_length.md @@ -3,12 +3,13 @@ Comment lines can optionally be ignored. The maximum allowed length is configurable. You can set constructs you want to fold with `CountAsOne`. -Available are: 'array', 'hash', 'heredoc', and 'method_call'. Each construct -will be counted as one line regardless of its actual size. + +Available are: 'array', 'hash', 'heredoc', and 'method_call'. +Each construct will be counted as one line regardless of its actual size. NOTE: This cop also applies for `Struct` definitions. -### Example: CountAsOne: ['array', 'heredoc', 'method_call'] +### Example: CountAsOne: ['array', 'hash', 'heredoc', 'method_call'] class Foo ARRAY = [ # +1 @@ -16,7 +17,7 @@ NOTE: This cop also applies for `Struct` definitions. 2 ] - HASH = { # +3 + HASH = { # +1 key: 'value' } @@ -29,4 +30,4 @@ NOTE: This cop also applies for `Struct` definitions. 1, 2 ) - end # 6 points + end # 4 points diff --git a/config/contents/metrics/cyclomatic_complexity.md b/config/contents/metrics/cyclomatic_complexity.md index 88bd324a..1db99e95 100644 --- a/config/contents/metrics/cyclomatic_complexity.md +++ b/config/contents/metrics/cyclomatic_complexity.md @@ -9,11 +9,14 @@ operator (or keyword and) can be converted to a nested if statement, and ||/or is shorthand for a sequence of ifs, so they also add one. Loops can be said to have an exit condition, so they add one. Blocks that are calls to builtin iteration methods -(e.g. `ary.map{...}) also add one, others are ignored. +(e.g. `ary.map{...}`) also add one, others are ignored. + +### Example: def each_child_node(*types) # count begins: 1 unless block_given? # unless: +1 return to_enum(__method__, *types) + end children.each do |child| # each{}: +1 next unless child.is_a?(Node) # unless: +1 diff --git a/config/contents/metrics/method_length.md b/config/contents/metrics/method_length.md index 281adfee..af114667 100644 --- a/config/contents/metrics/method_length.md +++ b/config/contents/metrics/method_length.md @@ -3,15 +3,16 @@ Comment lines can optionally be allowed. The maximum allowed length is configurable. You can set constructs you want to fold with `CountAsOne`. -Available are: 'array', 'hash', 'heredoc', and 'method_call'. Each construct -will be counted as one line regardless of its actual size. + +Available are: 'array', 'hash', 'heredoc', and 'method_call'. +Each construct will be counted as one line regardless of its actual size. NOTE: The `ExcludedMethods` and `IgnoredMethods` configuration is deprecated and only kept for backwards compatibility. Please use `AllowedMethods` and `AllowedPatterns` instead. By default, there are no methods to allowed. -### Example: CountAsOne: ['array', 'heredoc', 'method_call'] +### Example: CountAsOne: ['array', 'hash', 'heredoc', 'method_call'] def m array = [ # +1 @@ -19,7 +20,7 @@ By default, there are no methods to allowed. 2 ] - hash = { # +3 + hash = { # +1 key: 'value' } @@ -32,4 +33,4 @@ By default, there are no methods to allowed. 1, 2 ) - end # 6 points + end # 4 points diff --git a/config/contents/metrics/module_length.md b/config/contents/metrics/module_length.md index 653aecee..2cb7f667 100644 --- a/config/contents/metrics/module_length.md +++ b/config/contents/metrics/module_length.md @@ -3,10 +3,11 @@ Comment lines can optionally be ignored. The maximum allowed length is configurable. You can set constructs you want to fold with `CountAsOne`. -Available are: 'array', 'hash', 'heredoc', and 'method_call'. Each construct -will be counted as one line regardless of its actual size. -### Example: CountAsOne: ['array', 'heredoc', 'method_call'] +Available are: 'array', 'hash', 'heredoc', and 'method_call'. +Each construct will be counted as one line regardless of its actual size. + +### Example: CountAsOne: ['array', 'hash', 'heredoc', 'method_call'] module M ARRAY = [ # +1 @@ -14,7 +15,7 @@ will be counted as one line regardless of its actual size. 2 ] - HASH = { # +3 + HASH = { # +1 key: 'value' } @@ -27,4 +28,4 @@ will be counted as one line regardless of its actual size. 1, 2 ) - end # 6 points + end # 4 points diff --git a/config/contents/naming/accessor_method_name.md b/config/contents/naming/accessor_method_name.md index 327cf230..6efdd022 100644 --- a/config/contents/naming/accessor_method_name.md +++ b/config/contents/naming/accessor_method_name.md @@ -1,10 +1,10 @@ -Makes sure that accessor methods are named properly. Applies -to both instance and class methods. +Avoid prefixing accessor method names with `get_` or `set_`. +Applies to both instance and class methods. -NOTE: Offenses are only registered for methods with the expected -arity. Getters (`get_attribute`) must have no arguments to be -registered, and setters (`set_attribute(value)`) must have exactly -one. +NOTE: Method names starting with `get_` or `set_` only register an offense +when the methods match the expected arity for getters and setters respectively. +Getters (`get_attribute`) must have no arguments to be registered, +and setters (`set_attribute(value)`) must have exactly one. ### Example: # bad diff --git a/config/contents/naming/block_forwarding.md b/config/contents/naming/block_forwarding.md index f50c0457..adfdead5 100644 --- a/config/contents/naming/block_forwarding.md +++ b/config/contents/naming/block_forwarding.md @@ -8,6 +8,19 @@ You can specify the block variable name for autocorrection with `BlockForwarding The default variable name is `block`. If the name is already in use, it will not be autocorrected. +[NOTE] +==== +Because of a bug in Ruby 3.3.0, when a block is referenced inside of another block, +no offense will be registered until Ruby 3.4: + +```ruby +def foo(&block) + # Using an anonymous block would be a syntax error on Ruby 3.3.0 + block_method { bar(&block) } +end +``` +==== + ### Example: EnforcedStyle: anonymous (default) # bad diff --git a/config/contents/naming/method_name.md b/config/contents/naming/method_name.md index 097746de..49be85c4 100644 --- a/config/contents/naming/method_name.md +++ b/config/contents/naming/method_name.md @@ -1,14 +1,29 @@ Makes sure that all methods use the configured style, snake_case or camelCase, for their names. -This cop has `AllowedPatterns` configuration option. +Method names matching patterns are always allowed. - Naming/MethodName: - AllowedPatterns: - - '\AonSelectionBulkChange\z' - - '\AonSelectionCleared\z' +The cop can be configured with `AllowedPatterns` to allow certain regexp patterns: -Method names matching patterns are always allowed. +```yaml +Naming/MethodName: + AllowedPatterns: + - '\AonSelectionBulkChange\z' + - '\AonSelectionCleared\z' +``` + +As well, you can also forbid specific method names or regexp patterns +using `ForbiddenIdentifiers` or `ForbiddenPatterns`: + +```yaml +Naming/MethodName: + ForbiddenIdentifiers: + - 'def' + - 'super' + ForbiddenPatterns: + - '_v1\z' + - '_gen1\z' +``` ### Example: EnforcedStyle: snake_case (default) # bad @@ -22,4 +37,14 @@ Method names matching patterns are always allowed. def foo_bar; end # good - def fooBar; end \ No newline at end of file + def fooBar; end + +### Example: ForbiddenIdentifiers: ['def', 'super'] + # bad + def def; end + def super; end + +### Example: ForbiddenPatterns: ['_v1\z', '_gen1\z'] + # bad + def release_v1; end + def api_gen1; end diff --git a/config/contents/naming/predicate_name.md b/config/contents/naming/predicate_name.md index b3454b75..88066ac1 100644 --- a/config/contents/naming/predicate_name.md +++ b/config/contents/naming/predicate_name.md @@ -1,52 +1,96 @@ -Checks that predicate methods names end with a question mark and +Checks that predicate method names end with a question mark and do not start with a forbidden prefix. -A method is determined to be a predicate method if its name starts -with one of the prefixes defined in the `NamePrefix` configuration. -You can change what prefixes are considered by changing this option. -Any method name that starts with one of these prefixes is required by -the cop to end with a `?`. Other methods can be allowed by adding to -the `AllowedMethods` configuration. +A method is determined to be a predicate method if its name starts with +one of the prefixes listed in the `NamePrefix` configuration. The list +defaults to `is_`, `has_`, and `have_` but may be overridden. -NOTE: The `is_a?` method is allowed by default. +Predicate methods must end with a question mark. -If `ForbiddenPrefixes` is set, methods that start with the configured -prefixes will not be allowed and will be removed by autocorrection. +When `ForbiddenPrefixes` is also set (as it is by default), predicate +methods which begin with a forbidden prefix are not allowed, even if +they end with a `?`. These methods should be changed to remove the +prefix. -In other words, if `ForbiddenPrefixes` is empty, a method named `is_foo` -will register an offense only due to the lack of question mark (and will be -autocorrected to `is_foo?`). If `ForbiddenPrefixes` contains `is_`, -`is_foo` will register an offense both because the ? is missing and because of -the `is_` prefix, and will be corrected to `foo?`. +When `UseSorbetSigs` set to true (optional), the cop will only report +offenses if the method has a Sorbet `sig` with a return type of +`T::Boolean`. Dynamic methods are not supported with this configuration. -NOTE: `ForbiddenPrefixes` is only applied to prefixes in `NamePrefix`; -a prefix in the former but not the latter will not be considered by -this cop. - -### Example: +### Example: NamePrefix: ['is_', 'has_', 'have_'] (default) # bad def is_even(value) end - def is_even?(value) + # When ForbiddenPrefixes: ['is_', 'has_', 'have_'] (default) + # good + def even?(value) end + # When ForbiddenPrefixes: [] # good - def even?(value) + def is_even?(value) end +### Example: NamePrefix: ['seems_to_be_'] # bad - def has_value + def seems_to_be_even(value) end - def has_value? + # When ForbiddenPrefixes: ['seems_to_be_'] + # good + def even?(value) end + # When ForbiddenPrefixes: [] # good - def value? + def seems_to_be_even?(value) end ### Example: AllowedMethods: ['is_a?'] (default) + # Despite starting with the `is_` prefix, this method is allowed # good def is_a?(value) end + +### Example: AllowedMethods: ['is_even?'] + # good + def is_even?(value) + end + +### Example: UseSorbetSigs: false (default) + # bad + sig { returns(String) } + def is_this_thing_on + "yes" + end + + # good - Sorbet signature is not evaluated + sig { returns(String) } + def is_this_thing_on? + "yes" + end + +### Example: UseSorbetSigs: true + # bad + sig { returns(T::Boolean) } + def odd(value) + end + + # good + sig { returns(T::Boolean) } + def odd?(value) + end + +### Example: MethodDefinitionMacros: ['define_method', 'define_singleton_method'] (default) + # bad + define_method(:is_even) { |value| } + + # good + define_method(:even?) { |value| } + +### Example: MethodDefinitionMacros: ['def_node_matcher'] + # bad + def_node_matcher(:is_even) { |value| } + + # good + def_node_matcher(:even?) { |value| } diff --git a/config/contents/naming/variable_name.md b/config/contents/naming/variable_name.md index 01f16eaf..71c17821 100644 --- a/config/contents/naming/variable_name.md +++ b/config/contents/naming/variable_name.md @@ -1,5 +1,12 @@ -Makes sure that all variables use the configured style, -snake_case or camelCase, for their names. +Checks that the configured style (snake_case or camelCase) is used for all variable names. +This includes local variables, instance variables, class variables, method arguments +(positional, keyword, rest or block), and block arguments. + +The cop can also be configured to forbid using specific names for variables, using +`ForbiddenIdentifiers` or `ForbiddenPatterns`. In addition to the above, this applies +to global variables as well. + +Method definitions and method calls are not affected by this cop. ### Example: EnforcedStyle: snake_case (default) # bad @@ -21,4 +28,18 @@ snake_case or camelCase, for their names. ### Example: AllowedPatterns: ['_v\d+\z'] # good (with EnforcedStyle: camelCase) - :release_v1 + release_v1 = true + +### Example: ForbiddenIdentifiers: ['fooBar'] + # bad (in all cases) + fooBar = 1 + @fooBar = 1 + @@fooBar = 1 + $fooBar = 1 + +### Example: ForbiddenPatterns: ['_v\d+\z'] + # bad (in all cases) + release_v1 = true + @release_v1 = true + @@release_v1 = true + $release_v1 = true diff --git a/config/contents/style/access_modifier_declarations.md b/config/contents/style/access_modifier_declarations.md index 0811a77b..ffeda0cf 100644 --- a/config/contents/style/access_modifier_declarations.md +++ b/config/contents/style/access_modifier_declarations.md @@ -64,6 +64,9 @@ the group access modifier. class Foo private :bar, :baz + private *%i[qux quux] + private *METHOD_NAMES + private *private_methods end @@ -72,6 +75,9 @@ the group access modifier. class Foo private :bar, :baz + private *%i[qux quux] + private *METHOD_NAMES + private *private_methods end @@ -101,4 +107,24 @@ the group access modifier. private attr_accessor :qux private attr :quux + end + +### Example: AllowModifiersOnAliasMethod: true (default) + # good + class Foo + + public alias_method :bar, :foo + protected alias_method :baz, :foo + private alias_method :qux, :foo + + end + +### Example: AllowModifiersOnAliasMethod: false + # bad + class Foo + + public alias_method :bar, :foo + protected alias_method :baz, :foo + private alias_method :qux, :foo + end \ No newline at end of file diff --git a/config/contents/style/accessor_grouping.md b/config/contents/style/accessor_grouping.md index bb8c0c81..30fba283 100644 --- a/config/contents/style/accessor_grouping.md +++ b/config/contents/style/accessor_grouping.md @@ -5,6 +5,9 @@ but it can be configured to enforce separating them in multiple declarations. NOTE: If there is a method call before the accessor method it is always allowed as it might be intended like Sorbet. +NOTE: If there is a RBS::Inline annotation comment just after the accessor method +it is always allowed. + ### Example: EnforcedStyle: grouped (default) # bad class Foo diff --git a/config/contents/style/ambiguous_endless_method_definition.md b/config/contents/style/ambiguous_endless_method_definition.md new file mode 100644 index 00000000..012d60f1 --- /dev/null +++ b/config/contents/style/ambiguous_endless_method_definition.md @@ -0,0 +1,23 @@ +Looks for endless methods inside operations of lower precedence (`and`, `or`, and +modifier forms of `if`, `unless`, `while`, `until`) that are ambiguous due to +lack of parentheses. This may lead to unexpected behavior as the code may appear +to use these keywords as part of the method but in fact they modify +the method definition itself. + +In these cases, using a normal method definition is more clear. + +### Example: + + # bad + def foo = true if bar + + # good - using a non-endless method is more explicit + def foo + true + end if bar + + # ok - method body is explicit + def foo = (true if bar) + + # ok - method definition is explicit + (def foo = true) if bar \ No newline at end of file diff --git a/config/contents/style/arguments_forwarding.md b/config/contents/style/arguments_forwarding.md index 337c91cc..059ca35f 100644 --- a/config/contents/style/arguments_forwarding.md +++ b/config/contents/style/arguments_forwarding.md @@ -26,6 +26,19 @@ Names not on this list are likely to be meaningful and are allowed by default. This cop handles not only method forwarding but also forwarding to `super`. +[NOTE] +==== +Because of a bug in Ruby 3.3.0, when a block is referenced inside of another block, +no offense will be registered until Ruby 3.4: + +```ruby +def foo(&block) + # Using an anonymous block would be a syntax error on Ruby 3.3.0 + block_method { bar(&block) } +end +``` +==== + ### Example: # bad def foo(*args, &block) diff --git a/config/contents/style/array_intersect.md b/config/contents/style/array_intersect.md index 41eedc95..25a7e048 100644 --- a/config/contents/style/array_intersect.md +++ b/config/contents/style/array_intersect.md @@ -1,7 +1,8 @@ In Ruby 3.1, `Array#intersect?` has been added. This cop identifies places where `(array1 & array2).any?` -can be replaced by `array1.intersect?(array2)`. +or `(array1.intersection(array2)).any?` can be replaced by +`array1.intersect?(array2)`. The `array1.intersect?(array2)` method is faster than `(array1 & array2).any?` and is more readable. @@ -14,6 +15,10 @@ so it will not be detected when using block argument. [1].intersect?([1,2]) { |x| false } # => true ``` +NOTE: Although `Array#intersection` can take zero or multiple arguments, +only cases where exactly one argument is provided can be replaced with +`Array#intersect?` and are handled by this cop. + ### Safety: This cop cannot guarantee that `array1` and `array2` are @@ -23,6 +28,12 @@ actually arrays while method `intersect?` is for arrays only. # bad (array1 & array2).any? (array1 & array2).empty? + (array1 & array2).none? + + # bad + array1.intersection(array2).any? + array1.intersection(array2).empty? + array1.intersection(array2).none? # good array1.intersect?(array2) diff --git a/config/contents/style/bitwise_predicate.md b/config/contents/style/bitwise_predicate.md new file mode 100644 index 00000000..2f47d517 --- /dev/null +++ b/config/contents/style/bitwise_predicate.md @@ -0,0 +1,26 @@ +Prefer bitwise predicate methods over direct comparison operations. + +### Safety: + +This cop is unsafe, as it can produce false positives if the receiver +is not an `Integer` object. + +### Example: + + # bad - checks any set bits + (variable & flags).positive? + + # good + variable.anybits?(flags) + + # bad - checks all set bits + (variable & flags) == flags + + # good + variable.allbits?(flags) + + # bad - checks no set bits + (variable & flags).zero? + + # good + variable.nobits?(flags) diff --git a/config/contents/style/class_and_module_children.md b/config/contents/style/class_and_module_children.md index 22cbf65f..dfac64bf 100644 --- a/config/contents/style/class_and_module_children.md +++ b/config/contents/style/class_and_module_children.md @@ -1,12 +1,24 @@ -Checks the style of children definitions at classes and -modules. Basically there are two different styles: +Checks that namespaced classes and modules are defined with a consistent style. + +With `nested` style, classes and modules should be defined separately (one constant +on each line, without `::`). With `compact` style, classes and modules should be +defined with fully qualified names (using `::` for namespaces). + +NOTE: The style chosen will affect `Module.nesting` for the class or module. Using +`nested` style will result in each level being added, whereas `compact` style will +only include the fully qualified class or module name. + +By default, `EnforcedStyle` applies to both classes and modules. If desired, separate +styles can be defined for classes and modules by using `EnforcedStyleForClasses` and +`EnforcedStyleForModules` respectively. If not set, or set to nil, the `EnforcedStyle` +value will be used. ### Safety: 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 +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 have the knowledge to perform either operation safely and thus requires manual oversight. diff --git a/config/contents/style/collection_compact.md b/config/contents/style/collection_compact.md index 666584ad..81cdab63 100644 --- a/config/contents/style/collection_compact.md +++ b/config/contents/style/collection_compact.md @@ -17,6 +17,7 @@ when the receiver is a hash object. array.reject(&:nil?) array.reject { |e| e.nil? } array.select { |e| !e.nil? } + array.filter { |e| !e.nil? } array.grep_v(nil) array.grep_v(NilClass) @@ -25,10 +26,9 @@ when the receiver is a hash object. # bad hash.reject!(&:nil?) - array.delete_if(&:nil?) hash.reject! { |k, v| v.nil? } - array.delete_if { |e| e.nil? } hash.select! { |k, v| !v.nil? } + hash.filter! { |k, v| !v.nil? } # good hash.compact! diff --git a/config/contents/style/combinable_defined.md b/config/contents/style/combinable_defined.md new file mode 100644 index 00000000..2d8c41a2 --- /dev/null +++ b/config/contents/style/combinable_defined.md @@ -0,0 +1,18 @@ +Checks for multiple `defined?` calls joined by `&&` that can be combined +into a single `defined?`. + +When checking that a nested constant or chained method is defined, it is +not necessary to check each ancestor or component of the chain. + +### Example: + # bad + defined?(Foo) && defined?(Foo::Bar) && defined?(Foo::Bar::Baz) + + # good + defined?(Foo::Bar::Baz) + + # bad + defined?(foo) && defined?(foo.bar) && defined?(foo.bar.baz) + + # good + defined?(foo.bar.baz) \ No newline at end of file diff --git a/config/contents/style/combinable_loops.md b/config/contents/style/combinable_loops.md index 6f1c437e..8e78f060 100644 --- a/config/contents/style/combinable_loops.md +++ b/config/contents/style/combinable_loops.md @@ -2,6 +2,9 @@ Checks for places where multiple consecutive loops over the same data can be combined into a single loop. It is very likely that combining them will make the code more efficient and more concise. +NOTE: Autocorrection is not applied when the block variable names differ in separate loops, +as it is impossible to determine which variable name should be prioritized. + ### Safety: The cop is unsafe, because the first loop might modify state that the diff --git a/config/contents/style/commented_keyword.md b/config/contents/style/commented_keyword.md index 0b1b9c1d..663d2ab7 100644 --- a/config/contents/style/commented_keyword.md +++ b/config/contents/style/commented_keyword.md @@ -2,8 +2,8 @@ Checks for comments put on the same line as some keywords. These keywords are: `class`, `module`, `def`, `begin`, `end`. Note that some comments -(`:nodoc:`, `:yields:`, `rubocop:disable` and `rubocop:todo`) -are allowed. +(`:nodoc:`, `:yields:`, `rubocop:disable` and `rubocop:todo`), +RBS::Inline annotation, and Steep annotation (`steep:ignore`) are allowed. Autocorrection removes comments from `end` keyword and keeps comments for `class`, `module`, `def` and `begin` above the keyword. diff --git a/config/contents/style/comparable_between.md b/config/contents/style/comparable_between.md new file mode 100644 index 00000000..2b0c4da1 --- /dev/null +++ b/config/contents/style/comparable_between.md @@ -0,0 +1,16 @@ +Checks for logical comparison which can be replaced with `Comparable#between?`. + +NOTE: `Comparable#between?` is on average slightly slower than logical comparison, +although the difference generally isn't observable. If you require maximum +performance, consider using logical comparison. + +### Example: + + # bad + x >= min && x <= max + + # bad + x <= max && x >= min + + # good + x.between?(min, max) diff --git a/config/contents/style/def_with_parentheses.md b/config/contents/style/def_with_parentheses.md index eb2f930f..d4f5569a 100644 --- a/config/contents/style/def_with_parentheses.md +++ b/config/contents/style/def_with_parentheses.md @@ -23,8 +23,6 @@ class/singleton methods are checked. # good (without parentheses it's a syntax error) def foo() do_something end -### Example: - # bad def Baz.foo() do_something diff --git a/config/contents/style/dig_chain.md b/config/contents/style/dig_chain.md new file mode 100644 index 00000000..701ecc66 --- /dev/null +++ b/config/contents/style/dig_chain.md @@ -0,0 +1,19 @@ +Check for chained `dig` calls that can be collapsed into a single `dig`. + +### Safety: + +This cop is unsafe because it cannot be guaranteed that the receiver +is an `Enumerable` or does not have a nonstandard implementation +of `dig`. + +### Example: + # bad + x.dig(:foo).dig(:bar).dig(:baz) + x.dig(:foo, :bar).dig(:baz) + x.dig(:foo, :bar)&.dig(:baz) + + # good + x.dig(:foo, :bar, :baz) + + # good - `dig`s cannot be combined + x.dig(:foo).bar.dig(:baz) diff --git a/config/contents/style/each_for_simple_loop.md b/config/contents/style/each_for_simple_loop.md index 7a8523de..e80bb896 100644 --- a/config/contents/style/each_for_simple_loop.md +++ b/config/contents/style/each_for_simple_loop.md @@ -1,5 +1,5 @@ Checks for loops which iterate a constant number of times, -using a Range literal and `#each`. This can be done more readably using +using a `Range` literal and `#each`. This can be done more readably using `Integer#times`. This check only applies if the block takes no parameters. @@ -11,7 +11,6 @@ This check only applies if the block takes no parameters. # good 5.times { } -### Example: # bad (0...10).each {} diff --git a/config/contents/style/empty_literal.md b/config/contents/style/empty_literal.md index d1ee0d5b..c5dd06a8 100644 --- a/config/contents/style/empty_literal.md +++ b/config/contents/style/empty_literal.md @@ -1,10 +1,16 @@ Checks for the use of a method, the result of which would be a literal, like an empty array, hash, or string. +NOTE: When frozen string literals are enabled, `String.new` +isn't corrected to an empty string since the former is +mutable and the latter would be frozen. + ### Example: # bad a = Array.new + a = Array[] h = Hash.new + h = Hash[] s = String.new # good diff --git a/config/contents/style/endless_method.md b/config/contents/style/endless_method.md index 9c650804..020bf33e 100644 --- a/config/contents/style/endless_method.md +++ b/config/contents/style/endless_method.md @@ -1,7 +1,10 @@ Checks for endless methods. -It can enforce either the use of endless methods definitions -for single-lined method bodies, or disallow endless methods. +It can enforce endless method definitions whenever possible or with single line methods. +It can also disallow multiline endless method definitions or all endless definitions. + +`require_single_line` style enforces endless method definitions for single line methods. +`require_always` style enforces endless method definitions for single statement methods. Other method definition types are not considered by this cop. @@ -10,33 +13,113 @@ The supported styles are: * allow_single_line (default) - only single line endless method definitions are allowed. * allow_always - all endless method definitions are allowed. * disallow - all endless method definitions are disallowed. +* require_single_line - endless method definitions are required for single line methods. +* require_always - all endless method definitions are required. NOTE: Incorrect endless method definitions will always be corrected to a multi-line definition. ### Example: EnforcedStyle: allow_single_line (default) + # bad, multi-line endless method + def my_method = x.foo + .bar + .baz + # good - def my_method() = x + def my_method + x + end - # bad, multi-line endless method - def my_method() = x.foo - .bar - .baz + # good + def my_method = x + + # good + def my_method + x.foo + .bar + .baz + end ### Example: EnforcedStyle: allow_always # good - def my_method() = x + def my_method + x + end + + # good + def my_method = x + + # good + def my_method = x.foo + .bar + .baz # good - def my_method() = x.foo - .bar - .baz + def my_method + x.foo + .bar + .baz + end ### Example: EnforcedStyle: disallow # bad - def my_method() = x + def my_method = x # bad - def my_method() = x.foo - .bar - .baz + def my_method = x.foo + .bar + .baz + + # good + def my_method + x + end + + # good + def my_method + x.foo + .bar + .baz + end + +### Example: EnforcedStyle: require_single_line + # bad + def my_method + x + end + + # bad + def my_method = x.foo + .bar + .baz + + # good + def my_method = x + + # good + def my_method + x.foo + .bar + .baz + end + +### Example: EnforcedStyle: require_always + # bad + def my_method + x + end + + # bad + def my_method + x.foo + .bar + .baz + end + + # good + def my_method = x + + # good + def my_method = x.foo + .bar + .baz diff --git a/config/contents/style/eval_with_location.md b/config/contents/style/eval_with_location.md index f441417b..fff337a1 100644 --- a/config/contents/style/eval_with_location.md +++ b/config/contents/style/eval_with_location.md @@ -12,6 +12,17 @@ values. However, if `eval` is called without a binding argument, the cop will not attempt to automatically add a binding, or add filename and line values. +NOTE: This cop works only when a string literal is given as a code string. +No offense is reported if a string variable is given as below: + +```ruby +code = <<-RUBY + def do_something + end +RUBY +eval code # not checked. +``` + ### Example: # bad eval <<-RUBY @@ -36,14 +47,3 @@ line values. def do_something end RUBY - -This cop works only when a string literal is given as a code string. -No offense is reported if a string variable is given as below: - -### Example: - # not checked - code = <<-RUBY - def do_something - end - RUBY - eval code diff --git a/config/contents/style/exact_regexp_match.md b/config/contents/style/exact_regexp_match.md index dda0c37c..b4952ab3 100644 --- a/config/contents/style/exact_regexp_match.md +++ b/config/contents/style/exact_regexp_match.md @@ -1,4 +1,4 @@ -Checks for exact regexp match inside Regexp literals. +Checks for exact regexp match inside `Regexp` literals. ### Example: diff --git a/config/contents/style/exponential_notation.md b/config/contents/style/exponential_notation.md index 3d813484..c3f057fe 100644 --- a/config/contents/style/exponential_notation.md +++ b/config/contents/style/exponential_notation.md @@ -3,7 +3,7 @@ for numbers in the code (eg 1.2e4). Different styles are supported: * `scientific` which enforces a mantissa between 1 (inclusive) and 10 (exclusive). * `engineering` which enforces the exponent to be a multiple of 3 and the mantissa - to be between 0.1 (inclusive) and 10 (exclusive). + to be between 0.1 (inclusive) and 1000 (exclusive). * `integral` which enforces the mantissa to always be a whole number without trailing zeroes. diff --git a/config/contents/style/fetch_env_var.md b/config/contents/style/fetch_env_var.md index 121c61f4..d1e8ecd4 100644 --- a/config/contents/style/fetch_env_var.md +++ b/config/contents/style/fetch_env_var.md @@ -1,7 +1,7 @@ Suggests `ENV.fetch` for the replacement of `ENV[]`. `ENV[]` silently fails and returns `nil` when the environment variable is unset, which may cause unexpected behaviors when the developer forgets to set it. -On the other hand, `ENV.fetch` raises KeyError or returns the explicitly +On the other hand, `ENV.fetch` raises `KeyError` or returns the explicitly specified default value. ### Example: diff --git a/config/contents/style/file_null.md b/config/contents/style/file_null.md new file mode 100644 index 00000000..ed2c5d1c --- /dev/null +++ b/config/contents/style/file_null.md @@ -0,0 +1,39 @@ +Use `File::NULL` instead of hardcoding the null device (`/dev/null` on Unix-like +OSes, `NUL` or `NUL:` on Windows), so that code is platform independent. +Only looks for full string matches, substrings within a longer string are not +considered. + +However, only files that use the string `'/dev/null'` are targeted for detection. +This is because the string `'NUL'` is not limited to the null device. +This behavior results in false negatives when the `'/dev/null'` string is not used, +but it is a trade-off to avoid false positives. `NULL:` +Unlike `'NUL'`, `'NUL:'` is regarded as something like `C:` and is always detected. + +NOTE: Uses inside arrays and hashes are ignored. + +### Safety: + +It is possible for a string value to be changed if code is being run +on multiple platforms and was previously hardcoded to a specific null device. + +For example, the following string will change on Windows when changed to +`File::NULL`: + +```ruby +path = "/dev/null" +``` + +### Example: + # bad + '/dev/null' + 'NUL' + 'NUL:' + + # good + File::NULL + + # ok - inside an array + null_devices = %w[/dev/null nul] + + # ok - inside a hash + { unix: "/dev/null", windows: "nul" } \ No newline at end of file diff --git a/config/contents/style/file_read.md b/config/contents/style/file_read.md index 0a6dcf51..af38459b 100644 --- a/config/contents/style/file_read.md +++ b/config/contents/style/file_read.md @@ -1,8 +1,7 @@ Favor `File.(bin)read` convenience methods. ### Example: - ## text mode - # bad + # bad - text mode File.open(filename).read File.open(filename, &:read) File.open(filename) { |f| f.read } @@ -18,9 +17,7 @@ Favor `File.(bin)read` convenience methods. # good File.read(filename) -### Example: - ## binary mode - # bad + # bad - binary mode File.open(filename, 'rb').read File.open(filename, 'rb', &:read) File.open(filename, 'rb') do |f| diff --git a/config/contents/style/file_touch.md b/config/contents/style/file_touch.md new file mode 100644 index 00000000..c6216d1b --- /dev/null +++ b/config/contents/style/file_touch.md @@ -0,0 +1,30 @@ +Checks for usage of `File.open` in append mode with empty block. + +Such a usage only creates a new file, but it doesn't update +timestamps for an existing file, which might have been the intention. + +For example, for an existing file `foo.txt`: + + ruby -e "puts File.mtime('foo.txt')" + # 2024-11-26 12:17:23 +0100 + + ruby -e "File.open('foo.txt', 'a') {}" + + ruby -e "puts File.mtime('foo.txt')" + # 2024-11-26 12:17:23 +0100 -> unchanged + +If the intention was to update timestamps, `FileUtils.touch('foo.txt')` +should be used instead. + +### Safety: + +Autocorrection is unsafe for this cop because unlike `File.open`, +`FileUtils.touch` updates an existing file's timestamps. + +### Example: + # bad + File.open(filename, 'a') {} + File.open(filename, 'a+') {} + + # good + FileUtils.touch(filename) diff --git a/config/contents/style/file_write.md b/config/contents/style/file_write.md index ceaf6c9f..679ff58b 100644 --- a/config/contents/style/file_write.md +++ b/config/contents/style/file_write.md @@ -11,8 +11,7 @@ end ``` ### Example: - ## text mode - # bad + # bad - text mode File.open(filename, 'w').write(content) File.open(filename, 'w') do |f| f.write(content) @@ -21,9 +20,7 @@ end # good File.write(filename, content) -### Example: - ## binary mode - # bad + # bad - binary mode File.open(filename, 'wb').write(content) File.open(filename, 'wb') do |f| f.write(content) diff --git a/config/contents/style/format_string_token.md b/config/contents/style/format_string_token.md index 132e186e..b455e9ca 100644 --- a/config/contents/style/format_string_token.md +++ b/config/contents/style/format_string_token.md @@ -1,13 +1,21 @@ -Use a consistent style for named format string tokens. +Use a consistent style for tokens within a format string. -NOTE: `unannotated` style cop only works for strings -which are passed as arguments to those methods: -`printf`, `sprintf`, `format`, `%`. -The reason is that _unannotated_ format is very similar -to encoded URLs or Date/Time formatting strings. +By default, all strings are evaluated. In some cases, this may be undesirable, +as they could be used as arguments to a method that does not consider +them to be tokens, but rather other identifiers or just part of the string. -This cop can be customized allowed methods with `AllowedMethods`. -By default, there are no methods to allowed. +`AllowedMethods` or `AllowedPatterns` can be configured with in order to mark specific +methods as always allowed, thereby avoiding an offense from the cop. By default, there +are no allowed methods. + +Additionally, the cop can be made conservative by configuring it with +`Mode: conservative` (default `aggressive`). In this mode, tokens (regardless +of `EnforcedStyle`) are only considered if used in the format string argument to the +methods `printf`, `sprintf`, `format` and `%`. + +NOTE: Tokens in the `unannotated` style (eg. `%s`) are always treated as if +configured with `Conservative: true`. This is done in order to prevent false positives, +because this format is very similar to encoded URLs or Date/Time formatting strings. ### Example: EnforcedStyle: annotated (default) @@ -76,3 +84,17 @@ if the number of them is less than or equals to # good redirect('foo/%{bar_id}') + +### Example: Mode: conservative, EnforcedStyle: annotated + # In `conservative` mode, offenses are only registered for strings + # given to a known formatting method. + + # good + "%{greeting}" + foo("%{greeting}") + + # bad + format("%{greeting}", greeting: 'Hello') + printf("%{greeting}", greeting: 'Hello') + sprintf("%{greeting}", greeting: 'Hello') + "%{greeting}" % { greeting: 'Hello' } diff --git a/config/contents/style/global_std_stream.md b/config/contents/style/global_std_stream.md index 6183f11d..b04bd88d 100644 --- a/config/contents/style/global_std_stream.md +++ b/config/contents/style/global_std_stream.md @@ -3,6 +3,9 @@ Enforces the use of `$stdout/$stderr/$stdin` instead of `STDOUT/STDERR/STDIN`. reassign (possibly to redirect some stream) constants in Ruby, you'll get an interpreter warning if you do so. +Additionally, `$stdout/$stderr/$stdin` can safely be accessed in a Ractor because they +are ractor-local, while `STDOUT/STDERR/STDIN` will raise `Ractor::IsolationError`. + ### Safety: Autocorrection is unsafe because `STDOUT` and `$stdout` may point to different diff --git a/config/contents/style/hash_each_methods.md b/config/contents/style/hash_each_methods.md index 3cdef8fd..c236aaff 100644 --- a/config/contents/style/hash_each_methods.md +++ b/config/contents/style/hash_each_methods.md @@ -1,4 +1,4 @@ -Checks for uses of `each_key` and `each_value` Hash methods. +Checks for uses of `each_key` and `each_value` `Hash` methods. NOTE: If you have an array of two-element arrays, you can put parentheses around the block arguments to indicate that you're not diff --git a/config/contents/style/hash_except.md b/config/contents/style/hash_except.md index ab9b2cf1..57ff7ef5 100644 --- a/config/contents/style/hash_except.md +++ b/config/contents/style/hash_except.md @@ -5,8 +5,10 @@ This cop should only be enabled on Ruby version 3.0 or higher. (`Hash#except` was added in Ruby 3.0.) For safe detection, it is limited to commonly used string and symbol comparisons -when used `==`. -And do not check `Hash#delete_if` and `Hash#keep_if` to change receiver object. +when using `==` or `!=`. + +This cop doesn't check for `Hash#delete_if` and `Hash#keep_if` because they +modify the receiver. ### Safety: @@ -19,9 +21,35 @@ is a `Hash` or responds to the replacement method. {foo: 1, bar: 2, baz: 3}.reject {|k, v| k == :bar } {foo: 1, bar: 2, baz: 3}.select {|k, v| k != :bar } {foo: 1, bar: 2, baz: 3}.filter {|k, v| k != :bar } - {foo: 1, bar: 2, baz: 3}.reject {|k, v| %i[foo bar].include?(k) } - {foo: 1, bar: 2, baz: 3}.select {|k, v| !%i[foo bar].include?(k) } - {foo: 1, bar: 2, baz: 3}.filter {|k, v| !%i[foo bar].include?(k) } + {foo: 1, bar: 2, baz: 3}.reject {|k, v| k.eql?(:bar) } + + # bad + {foo: 1, bar: 2, baz: 3}.reject {|k, v| %i[bar].include?(k) } + {foo: 1, bar: 2, baz: 3}.select {|k, v| !%i[bar].include?(k) } + {foo: 1, bar: 2, baz: 3}.filter {|k, v| !%i[bar].include?(k) } + + # good + {foo: 1, bar: 2, baz: 3}.except(:bar) + +### Example: AllCops:ActiveSupportExtensionsEnabled: false (default) + + # good + {foo: 1, bar: 2, baz: 3}.reject {|k, v| !%i[bar].exclude?(k) } + {foo: 1, bar: 2, baz: 3}.select {|k, v| %i[bar].exclude?(k) } + + # good + {foo: 1, bar: 2, baz: 3}.reject {|k, v| k.in?(%i[bar]) } + {foo: 1, bar: 2, baz: 3}.select {|k, v| !k.in?(%i[bar]) } + +### Example: AllCops:ActiveSupportExtensionsEnabled: true + + # bad + {foo: 1, bar: 2, baz: 3}.reject {|k, v| !%i[bar].exclude?(k) } + {foo: 1, bar: 2, baz: 3}.select {|k, v| %i[bar].exclude?(k) } + + # bad + {foo: 1, bar: 2, baz: 3}.reject {|k, v| k.in?(%i[bar]) } + {foo: 1, bar: 2, baz: 3}.select {|k, v| !k.in?(%i[bar]) } # good {foo: 1, bar: 2, baz: 3}.except(:bar) diff --git a/config/contents/style/hash_fetch_chain.md b/config/contents/style/hash_fetch_chain.md new file mode 100644 index 00000000..22c9810b --- /dev/null +++ b/config/contents/style/hash_fetch_chain.md @@ -0,0 +1,34 @@ +Use `Hash#dig` instead of chaining potentially null `fetch` calls. + +When `fetch(identifier, nil)` calls are chained on a hash, the expectation +is that each step in the chain returns either `nil` or another hash, +and in both cases, these can be simplified with a single call to `dig` with +multiple arguments. + +If the 2nd parameter is `{}` or `Hash.new`, an offense will also be registered, +as long as the final call in the chain is a nil value. If a non-nil value is given, +the chain will not be registered as an offense, as the default value cannot be safely +given with `dig`. + +NOTE: See `Style/DigChain` for replacing chains of `dig` calls with +a single method call. + +### Safety: + +This cop is unsafe because it cannot be guaranteed that the receiver +is a `Hash` or that `fetch` or `dig` have the expected standard implementation. + +### Example: + # bad + hash.fetch('foo', nil)&.fetch('bar', nil) + + # bad + # earlier members of the chain can return `{}` as long as the final `fetch` + # has `nil` as a default value + hash.fetch('foo', {}).fetch('bar', nil) + + # good + hash.dig('foo', 'bar') + + # ok - not handled by the cop since the final `fetch` value is non-nil + hash.fetch('foo', {}).fetch('bar', {}) diff --git a/config/contents/style/hash_slice.md b/config/contents/style/hash_slice.md new file mode 100644 index 00000000..c5e99c8f --- /dev/null +++ b/config/contents/style/hash_slice.md @@ -0,0 +1,55 @@ +Checks for usages of `Hash#reject`, `Hash#select`, and `Hash#filter` methods +that can be replaced with `Hash#slice` method. + +This cop should only be enabled on Ruby version 2.5 or higher. +(`Hash#slice` was added in Ruby 2.5.) + +For safe detection, it is limited to commonly used string and symbol comparisons +when using `==` or `!=`. + +This cop doesn't check for `Hash#delete_if` and `Hash#keep_if` because they +modify the receiver. + +### Safety: + +This cop is unsafe because it cannot be guaranteed that the receiver +is a `Hash` or responds to the replacement method. + +### Example: + + # bad + {foo: 1, bar: 2, baz: 3}.select {|k, v| k == :bar } + {foo: 1, bar: 2, baz: 3}.reject {|k, v| k != :bar } + {foo: 1, bar: 2, baz: 3}.filter {|k, v| k == :bar } + {foo: 1, bar: 2, baz: 3}.select {|k, v| k.eql?(:bar) } + + # bad + {foo: 1, bar: 2, baz: 3}.select {|k, v| %i[bar].include?(k) } + {foo: 1, bar: 2, baz: 3}.reject {|k, v| !%i[bar].include?(k) } + {foo: 1, bar: 2, baz: 3}.filter {|k, v| %i[bar].include?(k) } + + # good + {foo: 1, bar: 2, baz: 3}.slice(:bar) + +### Example: AllCops:ActiveSupportExtensionsEnabled: false (default) + + # good + {foo: 1, bar: 2, baz: 3}.select {|k, v| !%i[bar].exclude?(k) } + {foo: 1, bar: 2, baz: 3}.reject {|k, v| %i[bar].exclude?(k) } + + # good + {foo: 1, bar: 2, baz: 3}.select {|k, v| k.in?(%i[bar]) } + {foo: 1, bar: 2, baz: 3}.reject {|k, v| !k.in?(%i[bar]) } + +### Example: AllCops:ActiveSupportExtensionsEnabled: true + + # bad + {foo: 1, bar: 2, baz: 3}.select {|k, v| !%i[bar].exclude?(k) } + {foo: 1, bar: 2, baz: 3}.reject {|k, v| %i[bar].exclude?(k) } + + # bad + {foo: 1, bar: 2, baz: 3}.select {|k, v| k.in?(%i[bar]) } + {foo: 1, bar: 2, baz: 3}.reject {|k, v| !k.in?(%i[bar]) } + + # good + {foo: 1, bar: 2, baz: 3}.slice(:bar) diff --git a/config/contents/style/hash_syntax.md b/config/contents/style/hash_syntax.md index 778d5b74..d725f6c2 100644 --- a/config/contents/style/hash_syntax.md +++ b/config/contents/style/hash_syntax.md @@ -63,7 +63,7 @@ The supported styles are: {a: 1, b: 2} {:c => 3, 'd' => 4} -### Example: EnforcedShorthandSyntax: always (default) +### Example: EnforcedShorthandSyntax: always # bad {foo: foo, bar: bar} @@ -71,6 +71,9 @@ The supported styles are: # good {foo:, bar:} + # good - allowed to mix syntaxes + {foo:, bar: baz} + ### Example: EnforcedShorthandSyntax: never # bad @@ -79,7 +82,7 @@ The supported styles are: # good {foo: foo, bar: bar} -### Example: EnforcedShorthandSyntax: either +### Example: EnforcedShorthandSyntax: either (default) # good {foo: foo, bar: bar} diff --git a/config/contents/style/if_with_boolean_literal_branches.md b/config/contents/style/if_with_boolean_literal_branches.md index 14cb25a4..8ce1c191 100644 --- a/config/contents/style/if_with_boolean_literal_branches.md +++ b/config/contents/style/if_with_boolean_literal_branches.md @@ -39,7 +39,6 @@ will return a boolean value. Those methods can be allowed with `AllowedMethods` # good foo == bar -### Example: # bad if foo.do_something? true diff --git a/config/contents/style/it_assignment.md b/config/contents/style/it_assignment.md new file mode 100644 index 00000000..166e48f1 --- /dev/null +++ b/config/contents/style/it_assignment.md @@ -0,0 +1,18 @@ +Checks for assignments to a local `it` variable inside a block +where `it` can refer to the first anonymous parameter as of Ruby 3.4. + +Although Ruby allows reassigning `it` in these cases, it could +cause confusion if `it` is used as a block parameter elsewhere. +For consistency, this also applies to numblocks and blocks with +parameters, even though `it` cannot be used in those cases. + +### Example: + # bad + foo { it = 5 } + foo { |bar| it = bar } + foo { it = _2 } + + # good - use a different variable name + foo { var = 5 } + foo { |bar| var = bar } + foo { bar = _2 } \ No newline at end of file diff --git a/config/contents/style/it_block_parameter.md b/config/contents/style/it_block_parameter.md new file mode 100644 index 00000000..3c1f7dc5 --- /dev/null +++ b/config/contents/style/it_block_parameter.md @@ -0,0 +1,33 @@ +Checks for blocks with one argument where `it` block parameter can be used. + +It provides three `EnforcedStyle` options: + +1. `only_numbered_parameters` (default) ... Detects only numbered block parameters. +2. `always` ... Always uses the `it` block parameter. +3. `disallow` ... Disallows the `it` block parameter. + +A single numbered parameter is detected when `only_numbered_parameters` or `always`. + +### Example: EnforcedStyle: only_numbered_parameters (default) + # bad + block { do_something(_1) } + + # good + block { do_something(it) } + block { |named_param| do_something(named_param) } + +### Example: EnforcedStyle: always + # bad + block { do_something(_1) } + block { |named_param| do_something(named_param) } + + # good + block { do_something(it) } + +### Example: EnforcedStyle: disallow + # bad + block { do_something(it) } + + # good + block { do_something(_1) } + block { |named_param| do_something(named_param) } diff --git a/config/contents/style/keyword_arguments_merging.md b/config/contents/style/keyword_arguments_merging.md new file mode 100644 index 00000000..2a388d79 --- /dev/null +++ b/config/contents/style/keyword_arguments_merging.md @@ -0,0 +1,14 @@ +When passing an existing hash as keyword arguments, provide additional arguments +directly rather than using `merge`. + +Providing arguments directly is more performant than using `merge`, and +also leads to shorter and simpler code. + +### Example: + # bad + some_method(**opts.merge(foo: true)) + some_method(**opts.merge(other_opts)) + + # good + some_method(**opts, foo: true) + some_method(**opts, **other_opts) diff --git a/config/contents/style/map_compact_with_conditional_block.md b/config/contents/style/map_compact_with_conditional_block.md index dad637ce..5c18dc5b 100644 --- a/config/contents/style/map_compact_with_conditional_block.md +++ b/config/contents/style/map_compact_with_conditional_block.md @@ -1,10 +1,14 @@ Prefer `select` or `reject` over `map { ... }.compact`. +This cop also handles `filter_map { ... }`, similar to `map { ... }.compact`. ### Example: # bad array.map { |e| some_condition? ? e : next }.compact + # bad + array.filter_map { |e| some_condition? ? e : next } + # bad array.map do |e| if some_condition? diff --git a/config/contents/style/map_into_array.md b/config/contents/style/map_into_array.md index d52f2ae9..b56797d4 100644 --- a/config/contents/style/map_into_array.md +++ b/config/contents/style/map_into_array.md @@ -8,8 +8,10 @@ NOTE: The return value of `Enumerable#each` is `self`, whereas the return value of `Enumerable#map` is an `Array`. They are not autocorrected when a return value could be used because these types differ. -NOTE: It only detects when the mapping destination is a local variable -initialized as an empty array and referred to only by the pushing operation. +NOTE: It only detects when the mapping destination is either: +* a local variable initialized as an empty array and referred to only by the +pushing operation; +* or, if it is the single block argument to a `[].tap` block. This is because, if not, it's challenging to statically guarantee that the mapping destination variable remains an empty array: @@ -37,6 +39,14 @@ with a block, not all objects that have a `map` method return an array # good dest = src.map { |e| e * 2 } + # bad + [].tap do |dest| + src.each { |e| dest << e * 2 } + end + + # good + dest = src.map { |e| e * 2 } + # good - contains another operation dest = [] src.each { |e| dest << e * 2; puts e } diff --git a/config/contents/style/method_call_with_args_parentheses.md b/config/contents/style/method_call_with_args_parentheses.md index 94361dbf..eef76aae 100644 --- a/config/contents/style/method_call_with_args_parentheses.md +++ b/config/contents/style/method_call_with_args_parentheses.md @@ -1,5 +1,5 @@ Enforces the presence (default) or absence of parentheses in -method calls containing parameters. +method calls containing arguments. In the default style (require_parentheses), macro methods are allowed. Additional methods can be added to the `AllowedMethods` or @@ -56,6 +56,8 @@ Non-exhaustive list of examples: https://bugs.ruby-lang.org/issues/18396. - Parentheses are required in anonymous arguments, keyword arguments and block passing in Ruby 3.2. +- Parentheses are required when the first argument is a beginless range or + the last argument is an endless range. ### Example: EnforcedStyle: require_parentheses (default) diff --git a/config/contents/style/method_call_without_args_parentheses.md b/config/contents/style/method_call_without_args_parentheses.md index a2ed9c9a..e6feef93 100644 --- a/config/contents/style/method_call_without_args_parentheses.md +++ b/config/contents/style/method_call_without_args_parentheses.md @@ -1,7 +1,7 @@ Checks for unwanted parentheses in parameterless method calls. -This cop can be customized allowed methods with `AllowedMethods`. -By default, there are no methods to allowed. +This cop's allowed methods can be customized with `AllowedMethods`. +By default, there are no allowed methods. NOTE: This cop allows the use of `it()` without arguments in blocks, as in `0.times { it() }`, following `Lint/ItWithoutArgumentsInBlock` cop. diff --git a/config/contents/style/missing_respond_to_missing.md b/config/contents/style/missing_respond_to_missing.md index f22e0057..299dadf5 100644 --- a/config/contents/style/missing_respond_to_missing.md +++ b/config/contents/style/missing_respond_to_missing.md @@ -1,17 +1,46 @@ Checks for the presence of `method_missing` without also defining `respond_to_missing?`. +Not defining `respond_to_missing?` will cause metaprogramming +methods like `respond_to?` to behave unexpectedly: + +```ruby +class StringDelegator + def initialize(string) + @string = string + end + + def method_missing(name, *args) + @string.send(name, *args) + end +end + +delegator = StringDelegator.new("foo") +# Claims to not respond to `upcase`. +delegator.respond_to?(:upcase) # => false +# But you can call it. +delegator.upcase # => FOO +``` + ### Example: # bad def method_missing(name, *args) - # ... + if @delegate.respond_to?(name) + @delegate.send(name, *args) + else + super + end end # good def respond_to_missing?(name, include_private) - # ... + @delegate.respond_to?(name) || super end def method_missing(name, *args) - # ... + if @delegate.respond_to?(name) + @delegate.send(name, *args) + else + super + end end diff --git a/config/contents/style/mutable_constant.md b/config/contents/style/mutable_constant.md index a501354e..d8662ca1 100644 --- a/config/contents/style/mutable_constant.md +++ b/config/contents/style/mutable_constant.md @@ -14,7 +14,7 @@ From Ruby 3.0, this cop honours the magic comment acceptable value other than none, it will suppress the offenses raised by this cop. It enforces frozen state. -NOTE: Regexp and Range literals are frozen objects since Ruby 3.0. +NOTE: `Regexp` and `Range` literals are frozen objects since Ruby 3.0. NOTE: From Ruby 3.0, interpolated strings are not frozen when `# frozen-string-literal: true` is used, so this cop enforces explicit diff --git a/config/contents/style/next.md b/config/contents/style/next.md index c2437bf0..b25bc059 100644 --- a/config/contents/style/next.md +++ b/config/contents/style/next.md @@ -40,4 +40,35 @@ Use `next` to skip iteration instead of a condition at the end. [1, 2].each do |a| next unless a == 1 puts a - end \ No newline at end of file + end + +### Example: AllowConsecutiveConditionals: false (default) + # bad + [1, 2].each do |a| + if a == 1 + puts a + end + if a == 2 + puts a + end + end + + # good + [1, 2].each do |a| + if a == 1 + puts a + end + next unless a == 2 + puts a + end + +### Example: AllowConsecutiveConditionals: true + # good + [1, 2].each do |a| + if a == 1 + puts a + end + if a == 2 + puts a + end + end diff --git a/config/contents/style/numeric_predicate.md b/config/contents/style/numeric_predicate.md index 50902e37..c73b731d 100644 --- a/config/contents/style/numeric_predicate.md +++ b/config/contents/style/numeric_predicate.md @@ -3,8 +3,8 @@ Checks for usage of comparison operators (`==`, These can be replaced by their respective predicate methods. This cop can also be configured to do the reverse. -This cop can be customized allowed methods with `AllowedMethods`. -By default, there are no methods to allowed. +This cop's allowed methods can be customized with `AllowedMethods`. +By default, there are no allowed methods. This cop disregards `#nonzero?` as its value is truthy or falsey, but not `true` and `false`, and thus not always interchangeable with diff --git a/config/contents/style/one_line_conditional.md b/config/contents/style/one_line_conditional.md index 009c89cc..0691e551 100644 --- a/config/contents/style/one_line_conditional.md +++ b/config/contents/style/one_line_conditional.md @@ -1,6 +1,6 @@ Checks for uses of if/then/else/end constructs on a single line. -AlwaysCorrectToMultiline config option can be set to true to auto-convert all offenses to -multi-line constructs. When AlwaysCorrectToMultiline is false (default case) the +`AlwaysCorrectToMultiline` config option can be set to true to autocorrect all offenses to +multi-line constructs. When `AlwaysCorrectToMultiline` is false (default case) the autocorrect will first try converting them to ternary operators. ### Example: @@ -24,4 +24,22 @@ autocorrect will first try converting them to ternary operators. bar else baz - end \ No newline at end of file + end + +### Example: AlwaysCorrectToMultiline: false (default) + # bad + if cond then run else dont end + + # good + cond ? run : dont + +### Example: AlwaysCorrectToMultiline: true + # bad + if cond then run else dont end + + # good + if cond + run + else + dont + end diff --git a/config/contents/style/open_struct_use.md b/config/contents/style/open_struct_use.md index 30e1d0ae..c66236a5 100644 --- a/config/contents/style/open_struct_use.md +++ b/config/contents/style/open_struct_use.md @@ -1,13 +1,13 @@ -Flags uses of OpenStruct, as it is now officially discouraged +Flags uses of `OpenStruct`, as it is now officially discouraged to be used for performance, version compatibility, and potential security issues. ### Safety: - Note that this cop may flag false positives; for instance, the following legal use of a hand-rolled `OpenStruct` type would be considered an offense: -``` +[source,ruby] +----- module MyNamespace class OpenStruct # not the OpenStruct we're looking for end @@ -16,7 +16,7 @@ module MyNamespace OpenStruct.new # resolves to MyNamespace::OpenStruct end end -``` +----- ### Example: diff --git a/config/contents/style/quoted_symbols.md b/config/contents/style/quoted_symbols.md index b756462b..2e145ee6 100644 --- a/config/contents/style/quoted_symbols.md +++ b/config/contents/style/quoted_symbols.md @@ -4,7 +4,7 @@ cop is not enabled, the default `EnforcedStyle` is `single_quotes`. String interpolation is always kept in double quotes. -Note: `Lint/SymbolConversion` can be used in parallel to ensure that symbols +NOTE: `Lint/SymbolConversion` can be used in parallel to ensure that symbols are not quoted that don't need to be. This cop is for configuring the quoting style to use for symbols that require quotes. diff --git a/config/contents/style/raise_args.md b/config/contents/style/raise_args.md index 5dd4000a..19d435f8 100644 --- a/config/contents/style/raise_args.md +++ b/config/contents/style/raise_args.md @@ -1,15 +1,15 @@ -Checks the args passed to `fail` and `raise`. For exploded -style (default), it recommends passing the exception class and message -to `raise`, rather than construct an instance of the error. It will -still allow passing just a message, or the construction of an error -with more than one argument. +Checks the args passed to `fail` and `raise`. -The exploded style works identically, but with the addition that it -will also suggest constructing error objects when the exception is -passed multiple arguments. +Exploded style (default) enforces passing the exception class and message +arguments separately, rather than constructing an instance of the error. + +Compact style enforces constructing an error instance. + +Both styles allow passing just a message, or an error instance when there is more +than one argument. The exploded style has an `AllowedCompactTypes` configuration -option that takes an Array of exception name Strings. +option that takes an `Array` of exception name Strings. ### Safety: diff --git a/config/contents/style/redundant_condition.md b/config/contents/style/redundant_condition.md index acdfb1d3..d086fbad 100644 --- a/config/contents/style/redundant_condition.md +++ b/config/contents/style/redundant_condition.md @@ -1,5 +1,18 @@ Checks for unnecessary conditional expressions. +NOTE: Since the intention of the comment cannot be automatically determined, +autocorrection is not applied when a comment is used, as shown below: + +[source,ruby] +----- +if b + # Important note. + b +else + c +end +----- + ### Example: # bad a = b ? b : c @@ -7,7 +20,6 @@ Checks for unnecessary conditional expressions. # good a = b || c -### Example: # bad if b b @@ -24,3 +36,23 @@ Checks for unnecessary conditional expressions. elsif cond c end + + # bad + a.nil? ? true : a + + # good + a.nil? || a + + # bad + if a.nil? + true + else + a + end + + # good + a.nil? || a + +### Example: AllowedMethods: ['nonzero?'] (default) + # good + num.nonzero? ? true : false diff --git a/config/contents/style/redundant_current_directory_in_path.md b/config/contents/style/redundant_current_directory_in_path.md index 20f0c635..598f0518 100644 --- a/config/contents/style/redundant_current_directory_in_path.md +++ b/config/contents/style/redundant_current_directory_in_path.md @@ -1,4 +1,5 @@ -Checks for uses a redundant current directory in path. +Checks for paths given to `require_relative` that start with +the current directory (`./`), which can be omitted. ### Example: diff --git a/config/contents/style/redundant_exception.md b/config/contents/style/redundant_exception.md index c71ae1c9..3e9dcf81 100644 --- a/config/contents/style/redundant_exception.md +++ b/config/contents/style/redundant_exception.md @@ -1,4 +1,4 @@ -Checks for RuntimeError as the argument of raise/fail. +Checks for `RuntimeError` as the argument of `raise`/`fail`. ### Example: # bad diff --git a/config/contents/style/redundant_file_extension_in_require.md b/config/contents/style/redundant_file_extension_in_require.md index fa04126e..8f69e7f9 100644 --- a/config/contents/style/redundant_file_extension_in_require.md +++ b/config/contents/style/redundant_file_extension_in_require.md @@ -1,7 +1,7 @@ Checks for the presence of superfluous `.rb` extension in the filename provided to `require` and `require_relative`. -Note: If the extension is omitted, Ruby tries adding '.rb', '.so', +NOTE: If the extension is omitted, Ruby tries adding '.rb', '.so', and so on to the name until found. If the file named cannot be found, a `LoadError` will be raised. There is an edge case where `foo.so` file is loaded instead of a `LoadError` diff --git a/config/contents/style/redundant_format.md b/config/contents/style/redundant_format.md new file mode 100644 index 00000000..e43faa61 --- /dev/null +++ b/config/contents/style/redundant_format.md @@ -0,0 +1,47 @@ +Checks for calls to `Kernel#format` or `Kernel#sprintf` that are redundant. + +Calling `format` with only a single string or constant argument is redundant, +as it can be replaced by the string or constant itself. + +Also looks for `format` calls where the arguments are literals that can be +inlined into a string easily. This applies to the `%s`, `%d`, `%i`, `%u`, and +`%f` format specifiers. + +### Safety: + +This cop's autocorrection is unsafe because string object returned by +`format` and `sprintf` are never frozen. If `format('string')` is autocorrected to +`'string'`, `FrozenError` may occur when calling a destructive method like `String#<<`. +Consider using `'string'.dup` instead of `format('string')`. +Additionally, since the necessity of `dup` cannot be determined automatically, +this autocorrection is inherently unsafe. + +```ruby +# frozen_string_literal: true + +format('template').frozen? # => false +'template'.frozen? # => true +``` + +### Example: + + # bad + format('the quick brown fox jumps over the lazy dog.') + sprintf('the quick brown fox jumps over the lazy dog.') + + # good + 'the quick brown fox jumps over the lazy dog.' + + # bad + format(MESSAGE) + sprintf(MESSAGE) + + # good + MESSAGE + + # bad + format('%s %s', 'foo', 'bar') + sprintf('%s %s', 'foo', 'bar') + + # good + 'foo bar' diff --git a/config/contents/style/redundant_freeze.md b/config/contents/style/redundant_freeze.md index 614e39b9..d55a85c8 100644 --- a/config/contents/style/redundant_freeze.md +++ b/config/contents/style/redundant_freeze.md @@ -1,6 +1,6 @@ Check for uses of `Object#freeze` on immutable objects. -NOTE: Regexp and Range literals are frozen objects since Ruby 3.0. +NOTE: `Regexp` and `Range` literals are frozen objects since Ruby 3.0. NOTE: From Ruby 3.0, this cop allows explicit freezing of interpolated string literals when `# frozen-string-literal: true` is used. diff --git a/config/contents/style/redundant_initialize.md b/config/contents/style/redundant_initialize.md index 65a2b0d3..3ce0f712 100644 --- a/config/contents/style/redundant_initialize.md +++ b/config/contents/style/redundant_initialize.md @@ -6,6 +6,9 @@ an argument that accepts multiple values (`restarg`, `kwrestarg`, etc.) it will not register an offense, because it allows the initializer to take a different number of arguments as its superclass potentially does. +NOTE: If an initializer takes any arguments and has an empty body, RuboCop +assumes it to *not* be redundant. This is to prevent potential `ArgumentError`. + NOTE: If an initializer argument has a default value, RuboCop assumes it to *not* be redundant. @@ -15,8 +18,10 @@ initializer. ### Safety: -This cop is unsafe because if subclass overrides `initialize` method with -a different arity than superclass. +This cop is unsafe because removing an empty initializer may alter +the behavior of the code, particularly if the superclass initializer +raises an exception. In such cases, the empty initializer may act as +a safeguard to prevent unintended errors from propagating. ### Example: # bad @@ -64,6 +69,10 @@ a different arity than superclass. super end + # good (changes the parameter requirements) + def initialize(_) + end + # good (changes the parameter requirements) def initialize(*) end diff --git a/config/contents/style/redundant_interpolation_unfreeze.md b/config/contents/style/redundant_interpolation_unfreeze.md new file mode 100644 index 00000000..5e9676db --- /dev/null +++ b/config/contents/style/redundant_interpolation_unfreeze.md @@ -0,0 +1,14 @@ +Before Ruby 3.0, interpolated strings followed the frozen string literal +magic comment which sometimes made it necessary to explicitly unfreeze them. +Ruby 3.0 changed interpolated strings to always be unfrozen which makes +unfreezing them redundant. + +### Example: + # bad + +"#{foo} bar" + + # bad + "#{foo} bar".dup + + # good + "#{foo} bar" diff --git a/config/contents/style/redundant_regexp_character_class.md b/config/contents/style/redundant_regexp_character_class.md index f4ff4aae..40d35c50 100644 --- a/config/contents/style/redundant_regexp_character_class.md +++ b/config/contents/style/redundant_regexp_character_class.md @@ -1,4 +1,4 @@ -Checks for unnecessary single-element Regexp character classes. +Checks for unnecessary single-element `Regexp` character classes. ### Example: diff --git a/config/contents/style/redundant_regexp_escape.md b/config/contents/style/redundant_regexp_escape.md index 62d5c2ac..a5559867 100644 --- a/config/contents/style/redundant_regexp_escape.md +++ b/config/contents/style/redundant_regexp_escape.md @@ -1,4 +1,4 @@ -Checks for redundant escapes inside Regexp literals. +Checks for redundant escapes inside `Regexp` literals. ### Example: # bad diff --git a/config/contents/style/redundant_self_assignment.md b/config/contents/style/redundant_self_assignment.md index 59722df8..630d5271 100644 --- a/config/contents/style/redundant_self_assignment.md +++ b/config/contents/style/redundant_self_assignment.md @@ -17,9 +17,5 @@ its receiver in place. args += foo hash.merge!(other) - # bad - self.foo = foo.concat(ary) - # good foo.concat(ary) - self.foo += ary diff --git a/config/contents/style/return_nil_in_predicate_method_definition.md b/config/contents/style/return_nil_in_predicate_method_definition.md index 87b2416a..6c339fc9 100644 --- a/config/contents/style/return_nil_in_predicate_method_definition.md +++ b/config/contents/style/return_nil_in_predicate_method_definition.md @@ -1,4 +1,5 @@ -Checks if `return` or `return nil` is used in predicate method definitions. +Checks for predicate method definitions that return `nil`. +A predicate method should only return a boolean value. ### Safety: @@ -27,6 +28,24 @@ from `nil` to `false` could potentially lead to incompatibility issues. do_something? end + # bad + def foo? + if condition + nil + else + true + end + end + + # good + def foo? + if condition + false + else + true + end + end + ### Example: AllowedMethods: ['foo?'] # good def foo? diff --git a/config/contents/style/safe_navigation.md b/config/contents/style/safe_navigation.md index e8f50ad5..f817302f 100644 --- a/config/contents/style/safe_navigation.md +++ b/config/contents/style/safe_navigation.md @@ -12,9 +12,14 @@ of the method is. If this is converted to safe navigation, `foo&.bar` can start returning `nil` as well as what the method returns. -The default for `MaxChainLength` is `2` +The default for `MaxChainLength` is `2`. We have limited the cop to not register an offense for method chains -that exceed this option is set. +that exceed this option's value. + +NOTE: This cop will recognize offenses but not autocorrect code when the +right hand side (RHS) of the `&&` statement is an `||` statement +(eg. `foo && (foo.bar? || foo.baz?)`). It can be corrected +manually by removing the `foo &&` and adding `&.` to each `foo` on the RHS. ### Safety: diff --git a/config/contents/style/safe_navigation_chain_length.md b/config/contents/style/safe_navigation_chain_length.md new file mode 100644 index 00000000..89d04d95 --- /dev/null +++ b/config/contents/style/safe_navigation_chain_length.md @@ -0,0 +1,19 @@ +Enforces safe navigation chains length to not exceed the configured maximum. +The longer the chain is, the harder it becomes to track what on it could be +returning `nil`. + +There is a potential interplay with `Style/SafeNavigation` - if both are enabled +and their settings are "incompatible", one of the cops will complain about what +the other proposes. + +E.g. if `Style/SafeNavigation` is configured with `MaxChainLength: 2` (default) +and this cop is configured with `Max: 1`, then for `foo.bar.baz if foo` the former +will suggest `foo&.bar&.baz`, which is an offense for the latter. + +### Example: Max: 2 (default) + # bad + user&.address&.zip&.upcase + + # good + user&.address&.zip + user.address.zip if user diff --git a/config/contents/style/select_by_regexp.md b/config/contents/style/select_by_regexp.md index 1a97bf1b..d63237fe 100644 --- a/config/contents/style/select_by_regexp.md +++ b/config/contents/style/select_by_regexp.md @@ -1,4 +1,4 @@ -Looks for places where an subset of an Enumerable (array, +Looks for places where a subset of an Enumerable (array, range, set, etc.; see note below) is calculated based on a `Regexp` match, and suggests `grep` or `grep_v` instead. @@ -23,7 +23,7 @@ Additionally, the cop cannot guarantee that the receiver of so the correction may not be actually equivalent. ### Example: - # bad (select or find_all) + # bad (select, filter, or find_all) array.select { |x| x.match? /regexp/ } array.select { |x| /regexp/.match?(x) } array.select { |x| x =~ /regexp/ } diff --git a/config/contents/style/send_with_literal_method_name.md b/config/contents/style/send_with_literal_method_name.md index 613420a0..ff6b1330 100644 --- a/config/contents/style/send_with_literal_method_name.md +++ b/config/contents/style/send_with_literal_method_name.md @@ -2,6 +2,19 @@ Detects the use of the `public_send` method with a literal method name argument. Since the `send` method can be used to call private methods, by default, only the `public_send` method is detected. +NOTE: Writer methods with names ending in `=` are always permitted because their +behavior differs as follows: + +```ruby +def foo=(foo) + @foo = foo + 42 +end + +self.foo = 1 # => 1 +send(:foo=, 1) # => 42 +``` + ### Safety: This cop is not safe because it can incorrectly detect based on the receiver. diff --git a/config/contents/style/single_line_block_params.md b/config/contents/style/single_line_block_params.md index 8354fec4..2d61131c 100644 --- a/config/contents/style/single_line_block_params.md +++ b/config/contents/style/single_line_block_params.md @@ -5,7 +5,7 @@ For instance one can configure `reduce`(`inject`) to use |a, e| as parameters. Configuration option: Methods -Should be set to use this cop. Array of hashes, where each key is the +Should be set to use this cop. `Array` of hashes, where each key is the method name and value - array of argument names. ### Example: Methods: [{reduce: %w[a b]}] diff --git a/config/contents/style/single_line_do_end_block.md b/config/contents/style/single_line_do_end_block.md index c28fb3f9..0cd978be 100644 --- a/config/contents/style/single_line_do_end_block.md +++ b/config/contents/style/single_line_do_end_block.md @@ -1,8 +1,13 @@ Checks for single-line `do`...`end` block. In practice a single line `do`...`end` is autocorrected when `EnforcedStyle: semantic` -in `Style/BlockDelimiters`. The autocorrection maintains the `do` ... `end` syntax to -preserve semantics and does not change it to `{`...`}` block. +is configured for `Style/BlockDelimiters`. The autocorrection maintains the +`do` ... `end` syntax to preserve semantics and does not change it to `{`...`}` block. + +NOTE: If `InspectBlocks` is set to `true` for `Layout/RedundantLineBreak`, blocks will +be autocorrected to be on a single line if possible. This cop respects that configuration +by not registering an offense if it would subsequently cause a +`Layout/RedundantLineBreak` offense. ### Example: diff --git a/config/contents/style/single_line_methods.md b/config/contents/style/single_line_methods.md index 6cecdbf5..703570f2 100644 --- a/config/contents/style/single_line_methods.md +++ b/config/contents/style/single_line_methods.md @@ -3,9 +3,9 @@ It will accept single-line methods with no body. Endless methods added in Ruby 3.0 are also accepted by this cop. -If `Style/EndlessMethod` is enabled with `EnforcedStyle: allow_single_line` or -`allow_always`, single-line methods will be autocorrected to endless -methods if there is only one statement in the body. +If `Style/EndlessMethod` is enabled with `EnforcedStyle: allow_single_line`, `allow_always`, +`require_single_line`, or `require_always`, single-line methods will be autocorrected +to endless methods if there is only one statement in the body. ### Example: # bad diff --git a/config/contents/style/string_methods.md b/config/contents/style/string_methods.md index 6b10bd12..ed0e1d02 100644 --- a/config/contents/style/string_methods.md +++ b/config/contents/style/string_methods.md @@ -1,5 +1,5 @@ Enforces the use of consistent method names -from the String class. +from the `String` class. ### Example: # bad diff --git a/config/contents/style/super_arguments.md b/config/contents/style/super_arguments.md index e5ff00f2..542281f6 100644 --- a/config/contents/style/super_arguments.md +++ b/config/contents/style/super_arguments.md @@ -1,5 +1,25 @@ -Checks for redundant argument forwarding when calling super -with arguments identical to the method definition. +Checks for redundant argument forwarding when calling super with arguments identical to +the method definition. + +Using zero arity `super` within a `define_method` block results in `RuntimeError`: + +```ruby +def m + define_method(:foo) { super() } # => OK +end + +def m + define_method(:foo) { super } # => RuntimeError +end +``` + +Furthermore, any arguments accompanied by a block may potentially be delegating to +`define_method`, therefore, `super` used within these blocks will be allowed. +This approach might result in false negatives, yet ensuring safe detection takes precedence. + +NOTE: When forwarding the same arguments but replacing the block argument with a new inline +block, it is not necessary to explicitly list the non-block arguments. As such, an offense +will be registered in this case. ### Example: # bad @@ -22,6 +42,16 @@ with arguments identical to the method definition. super() end + # bad - forwarding with overridden block + def method(*args, **kwargs, &block) + super(*args, **kwargs) { do_something } + end + + # good - implicitly passing all non-block arguments + def method(*args, **kwargs, &block) + super { do_something } + end + # good - assigning to the block variable before calling super def method(&block) # Assigning to the block variable would pass the old value to super, diff --git a/config/contents/style/trailing_comma_in_arguments.md b/config/contents/style/trailing_comma_in_arguments.md index 8968babd..3c22f61b 100644 --- a/config/contents/style/trailing_comma_in_arguments.md +++ b/config/contents/style/trailing_comma_in_arguments.md @@ -2,12 +2,15 @@ Checks for trailing comma in argument lists. The supported styles are: * `consistent_comma`: Requires a comma after the last argument, -for all parenthesized method calls with arguments. +for all parenthesized multi-line method calls with arguments. * `comma`: Requires a comma after the last argument, but only for parenthesized method calls where each argument is on its own line. * `no_comma`: Requires that there is no comma after the last argument. +Regardless of style, trailing commas are not allowed in +single-line method calls. + ### Example: EnforcedStyleForMultiline: consistent_comma # bad method(1, 2,) diff --git a/config/contents/style/trailing_comma_in_array_literal.md b/config/contents/style/trailing_comma_in_array_literal.md index 381d9c21..f8bcd7cc 100644 --- a/config/contents/style/trailing_comma_in_array_literal.md +++ b/config/contents/style/trailing_comma_in_array_literal.md @@ -1,12 +1,13 @@ Checks for trailing comma in array literals. The configuration options are: -* `consistent_comma`: Requires a comma after the -last item of all non-empty, multiline array literals. -* `comma`: Requires a comma after last item in an array, -but only when each item is on its own line. -* `no_comma`: Does not require a comma after the -last item in an array +* `consistent_comma`: Requires a comma after the last item of all non-empty, multiline array +literals. +* `comma`: Requires a comma after the last item in an array, but only when each item is on +its own line. +* `diff_comma`: Requires a comma after the last item in an array, but only when that item is +followed by an immediate newline. +* `no_comma`: Does not require a comma after the last item in an array ### Example: EnforcedStyleForMultiline: consistent_comma # bad @@ -32,6 +33,14 @@ last item in an array 2, ] + # bad + a = [1, 2, + 3, 4] + + # good + a = [1, 2, + 3, 4,] + ### Example: EnforcedStyleForMultiline: comma # bad a = [1, 2,] @@ -67,6 +76,38 @@ last item in an array 2, ] +### Example: EnforcedStyleForMultiline: diff_comma + # bad + a = [1, 2,] + + # good + a = [1, 2] + + # good + a = [ + 1, 2, + 3, + ] + + # good + a = [ + 1, 2, 3, + ] + + # good + a = [ + 1, + 2, + ] + + # bad + a = [1, 2, + 3, 4,] + + # good + a = [1, 2, + 3, 4] + ### Example: EnforcedStyleForMultiline: no_comma (default) # bad a = [1, 2,] diff --git a/config/contents/style/trailing_comma_in_hash_literal.md b/config/contents/style/trailing_comma_in_hash_literal.md index 67dde317..b5a059b8 100644 --- a/config/contents/style/trailing_comma_in_hash_literal.md +++ b/config/contents/style/trailing_comma_in_hash_literal.md @@ -1,12 +1,13 @@ Checks for trailing comma in hash literals. The configuration options are: -* `consistent_comma`: Requires a comma after the -last item of all non-empty, multiline hash literals. -* `comma`: Requires a comma after the last item in a hash, -but only when each item is on its own line. -* `no_comma`: Does not require a comma after the -last item in a hash +* `consistent_comma`: Requires a comma after the last item of all non-empty, multiline hash +literals. +* `comma`: Requires a comma after the last item in a hash, but only when each item is on its +own line. +* `diff_comma`: Requires a comma after the last item in a hash, but only when that item is +followed by an immediate newline. +* `no_comma`: Does not require a comma after the last item in a hash ### Example: EnforcedStyleForMultiline: consistent_comma @@ -33,6 +34,14 @@ last item in a hash bar: 2, } + # bad + a = { foo: 1, bar: 2, + baz: 3, qux: 4 } + + # good + a = { foo: 1, bar: 2, + baz: 3, qux: 4, } + ### Example: EnforcedStyleForMultiline: comma # bad @@ -69,6 +78,39 @@ last item in a hash bar: 2, } +### Example: EnforcedStyleForMultiline: diff_comma + + # bad + a = { foo: 1, bar: 2, } + + # good + a = { foo: 1, bar: 2 } + + # good + a = { + foo: 1, bar: 2, + qux: 3, + } + + # good + a = { + foo: 1, bar: 2, qux: 3, + } + + # good + a = { + foo: 1, + bar: 2, + } + + # bad + a = { foo: 1, bar: 2, + baz: 3, qux: 4, } + + # good + a = { foo: 1, bar: 2, + baz: 3, qux: 4 } + ### Example: EnforcedStyleForMultiline: no_comma (default) # bad diff --git a/config/contents/style/while_until_do.md b/config/contents/style/while_until_do.md index 51f09080..5c9df77d 100644 --- a/config/contents/style/while_until_do.md +++ b/config/contents/style/while_until_do.md @@ -12,8 +12,6 @@ Checks for uses of `do` in multi-line `while/until` statements. do_something(x.pop) end -### Example: - # bad until x.empty? do do_something(x.pop) diff --git a/config/contents/style/while_until_modifier.md b/config/contents/style/while_until_modifier.md index bc2f6524..34aade80 100644 --- a/config/contents/style/while_until_modifier.md +++ b/config/contents/style/while_until_modifier.md @@ -11,7 +11,6 @@ configured in the `Layout/LineLength` cop. # good x += 1 while x < 10 -### Example: # bad until x > 10 x += 1 @@ -20,7 +19,6 @@ configured in the `Layout/LineLength` cop. # good x += 1 until x > 10 -### Example: # bad x += 100 while x < 500 # a long comment that makes code too long if it were a single line diff --git a/lib/cc/engine/file_list_resolver.rb b/lib/cc/engine/file_list_resolver.rb index da3fdb96..c5188f7e 100644 --- a/lib/cc/engine/file_list_resolver.rb +++ b/lib/cc/engine/file_list_resolver.rb @@ -3,7 +3,7 @@ module CC module Engine class FileListResolver - def initialize(root:, engine_config: {}, config_store:) + def initialize(root:, config_store:, engine_config: {}) @root = root @include_paths = engine_config["include_paths"] || ["./"] @config_store = config_store @@ -25,11 +25,9 @@ def expanded_list def absolute_include_paths @include_paths.map do |path| - begin - Pathname.new(path).realpath.to_s - rescue Errno::ENOENT - nil - end + Pathname.new(path).realpath.to_s + rescue Errno::ENOENT + nil end.compact end diff --git a/lib/cc/engine/fingerprint.rb b/lib/cc/engine/fingerprint.rb index f30cdabe..622bd54f 100644 --- a/lib/cc/engine/fingerprint.rb +++ b/lib/cc/engine/fingerprint.rb @@ -15,7 +15,7 @@ class Fingerprint Metrics/PerceivedComplexity ].freeze - URL_REGEX = / \(https?\:.+\)/ + URL_REGEX = / \(https?:.+\)/ LINES_REGEX = / \[.+\]$/ def initialize(path, cop_name, message) diff --git a/lib/cc/engine/issue.rb b/lib/cc/engine/issue.rb index 1e860d10..033f5b27 100644 --- a/lib/cc/engine/issue.rb +++ b/lib/cc/engine/issue.rb @@ -5,7 +5,7 @@ module CC module Engine class Issue < SimpleDelegator - MULTIPLIER_REGEX = %r{\[([\d\.]+)\/([\d\.]+)\]} + MULTIPLIER_REGEX = %r{\[([\d.]+)/([\d.]+)\]} DEFAULT_REMEDIATION_POINTS = 50_000 DEFAULT_BASE_POINTS = 200_000 DEFAULT_OVERAGE_POINTS = 50_000 @@ -18,7 +18,7 @@ def initialize(issue, path, cop_list: nil) end # rubocop:disable Metrics/MethodLength - def to_json + def to_json(*_args) hash = { type: "Issue", check_name: check_name, @@ -38,6 +38,7 @@ def to_json hash.to_json end + # rubocop:enable Metrics/MethodLength def check_name "Rubocop/#{cop_name}" diff --git a/lib/cc/engine/source_file.rb b/lib/cc/engine/source_file.rb index 31be980a..5b721239 100644 --- a/lib/cc/engine/source_file.rb +++ b/lib/cc/engine/source_file.rb @@ -11,7 +11,7 @@ def initialize(config_store:, io:, path:, root:) end def inspect - rubocop_team.inspect_file(processed_source).each do |offense| + rubocop_team.investigate(processed_source).offenses.each do |offense| next if offense.disabled? io.print Issue.new(offense, display_path).to_json @@ -26,7 +26,7 @@ def inspect def processed_source processed_source = RuboCop::ProcessedSource.from_file(path, target_ruby_version) processed_source.config = config_store if processed_source.respond_to?(:config=) - processed_source.registry = RuboCop::Cop::Cop.registry if processed_source.respond_to?(:registry=) + processed_source.registry = RuboCop::Cop::Registry.global if processed_source.respond_to?(:registry=) processed_source end @@ -35,7 +35,7 @@ def target_ruby_version end def rubocop_team - RuboCop::Cop::Team.new(RuboCop::Cop::Cop.registry, config_store, display_cop_names: false) + RuboCop::Cop::Team.mobilize(RuboCop::Cop::Registry.global, config_store, display_cop_names: false) end def display_path diff --git a/spec/cc/engine/category_parser_spec.rb b/spec/cc/engine/category_parser_spec.rb index ca3ed798..9efb0bb6 100644 --- a/spec/cc/engine/category_parser_spec.rb +++ b/spec/cc/engine/category_parser_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "spec_helper" require "cc/engine/category_parser" diff --git a/spec/cc/engine/content_resolver_spec.rb b/spec/cc/engine/content_resolver_spec.rb index 84d45565..77275555 100644 --- a/spec/cc/engine/content_resolver_spec.rb +++ b/spec/cc/engine/content_resolver_spec.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + require "spec_helper" require "rubocop" require "cc/engine/content_resolver" module CC::Engine describe ContentResolver do - cops = RuboCop::Cop::Cop.all + cops = RuboCop::Cop::Registry.all # The more docs the better -- feel free to unwhitelist cops and add readups whitelist = File.read("./spec/support/currently_undocumented_cops.txt").lines.map(&:chomp) @@ -17,7 +19,7 @@ module CC::Engine context "for #{cop.name}" do if whitelist.include?(cop.name) it "has no content" do - expect(ContentResolver.new(cop.name).content).to eq false + expect(ContentResolver.new(cop.name).content).to be false end else it "has content" do diff --git a/spec/cc/engine/file_list_resolver_spec.rb b/spec/cc/engine/file_list_resolver_spec.rb index 80edcb70..44c8f81a 100644 --- a/spec/cc/engine/file_list_resolver_spec.rb +++ b/spec/cc/engine/file_list_resolver_spec.rb @@ -1,12 +1,15 @@ +# frozen_string_literal: true + require "spec_helper" require "rubocop" require "cc/engine/file_list_resolver" module CC::Engine describe FileListResolver do - include FilesystemHelpers + include ::FilesystemHelpers before { @code = Dir.mktmpdir } + let(:rubocop_config) { RuboCop::ConfigStore.new } it "uses default include path" do @@ -25,7 +28,7 @@ module CC::Engine create_source_file("bin/some_script", "#!/usr/bin/env ruby") resolver = FileListResolver.new(root: @code, engine_config: {}, config_store: rubocop_config) - expect(resolver.expanded_list).to eq %w[a.rb bin/some_script].map { |fn| Pathname.new(fn).realpath.to_s } + expect(resolver.expanded_list).to eq(%w[a.rb bin/some_script].map { |fn| Pathname.new(fn).realpath.to_s }) end end diff --git a/spec/cc/engine/fingerprint_spec.rb b/spec/cc/engine/fingerprint_spec.rb index f99b4a2f..4dd5a0d5 100644 --- a/spec/cc/engine/fingerprint_spec.rb +++ b/spec/cc/engine/fingerprint_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "spec_helper" require "cc/engine/fingerprint" @@ -21,7 +23,7 @@ module CC::Engine computed = Fingerprint.new(path, cop_name, message).compute - expect(computed).not_to be + expect(computed).to be_nil end it "computes same fingerprint regardles of message url detail" do diff --git a/spec/cc/engine/issue_spec.rb b/spec/cc/engine/issue_spec.rb index 70ceec94..25ca0e1a 100644 --- a/spec/cc/engine/issue_spec.rb +++ b/spec/cc/engine/issue_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "spec_helper" require "cc/engine/issue" require "ostruct" diff --git a/spec/cc/engine/rubocop_spec.rb b/spec/cc/engine/rubocop_spec.rb index 5a7a0352..f53fe930 100644 --- a/spec/cc/engine/rubocop_spec.rb +++ b/spec/cc/engine/rubocop_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "spec_helper" require "cc/engine/rubocop" require "ostruct" @@ -5,7 +7,7 @@ module CC::Engine describe Rubocop do - include RubocopRunner + include ::RubocopRunner describe "#run" do it "analyzes ruby files using rubocop" do @@ -150,9 +152,9 @@ def method end it "handles different locations properly" do - pseudo_source_range_klass = RuboCop::Cop::Offense::const_get(:PseudoSourceRange) + pseudo_source_range_klass = RuboCop::Cop::Offense.const_get(:PseudoSourceRange) - allow_any_instance_of(RuboCop::Cop::Team).to receive(:inspect_file).and_return( + allow_any_instance_of(RuboCop::Cop::Team).to receive_message_chain(:investigate, :offenses).and_return( [ OpenStruct.new( location: pseudo_source_range_klass.new( @@ -230,7 +232,7 @@ def method i["description"] == "unexpected token tCOLON" end - expect(issue).to be nil + expect(issue).to be_nil end it "includes Ruby files even if they don't end with .rb" do diff --git a/spec/cc/engine/source_file_spec.rb b/spec/cc/engine/source_file_spec.rb deleted file mode 100644 index e69de29b..00000000 diff --git a/spec/rubocop/config_patch_spec.rb b/spec/rubocop/config_patch_spec.rb index 292c30a3..e2d5438d 100644 --- a/spec/rubocop/config_patch_spec.rb +++ b/spec/rubocop/config_patch_spec.rb @@ -4,7 +4,6 @@ module CC::Engine describe "Rubocop config patch" do - it "prevents config from raising on obsolete cops" do config = RuboCop::Config.new( { @@ -19,6 +18,7 @@ module CC::Engine expect { config.validate }.to_not raise_error end.to output(//).to_stderr end + it "warns about obsolete cops" do config = RuboCop::Config.new( { @@ -32,8 +32,6 @@ module CC::Engine expected = <<~EOM The `Layout/AlignArguments` cop has been renamed to `Layout/ArgumentAlignment`. (obsolete configuration found in .rubocop.yml, please update it) - unrecognized cop or department Layout/AlignArguments found in .rubocop.yml - Did you mean `Layout/HashAlignment`? EOM expect { config.validate }.to output(expected).to_stderr @@ -49,12 +47,9 @@ module CC::Engine ".rubocop.yml" ) - expected = <<~EOM The `Style/TrailingComma` cop has been removed. Please use `Style/TrailingCommaInArguments`, `Style/TrailingCommaInArrayLiteral` and/or `Style/TrailingCommaInHashLiteral` instead. (obsolete configuration found in .rubocop.yml, please update it) - unrecognized cop or department Style/TrailingComma found in .rubocop.yml - Did you mean `Style/TrailingCommaInArguments`? EOM expect { config.validate }.to output(expected).to_stderr diff --git a/spec/rubocop/cop_patches_spec.rb b/spec/rubocop/cop_patches_spec.rb index 021ef38e..3ead777f 100644 --- a/spec/rubocop/cop_patches_spec.rb +++ b/spec/rubocop/cop_patches_spec.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + require "spec_helper" module CC::Engine describe "Rubocop cops patch" do - include RubocopRunner + include ::RubocopRunner describe "Metrics::AbcSize patch" do it "includes complete method body for cyclomatic complexity issue" do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 0a03b9e4..67e4125a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "rspec" Dir.glob("spec/support/**/*.rb").each(&method(:load)) diff --git a/spec/support/filesystem_helpers.rb b/spec/support/filesystem_helpers.rb index 426a8cbc..143349de 100644 --- a/spec/support/filesystem_helpers.rb +++ b/spec/support/filesystem_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module FilesystemHelpers def create_source_file(path, content) abs_path = File.join(@code, path) diff --git a/spec/support/matchers.rb b/spec/support/matchers.rb index be553e8b..eec7d2fd 100644 --- a/spec/support/matchers.rb +++ b/spec/support/matchers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'rspec/expectations' RSpec::Matchers.define :include_fingerprint do |fingerprint| diff --git a/spec/support/rubocop_runner.rb b/spec/support/rubocop_runner.rb index 0ef47e5d..417b912a 100644 --- a/spec/support/rubocop_runner.rb +++ b/spec/support/rubocop_runner.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + require "cc/engine/rubocop" require "tmpdir" module RubocopRunner def self.included(example_group) - example_group.include FilesystemHelpers + example_group.include ::FilesystemHelpers example_group.around do |example| Dir.mktmpdir do |code| @code = code