diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index b803099a391a..4994e6ccb565 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,6 +1,7 @@ --- name: Bug Report about: Report an issue with RuboCop you've discovered. +labels: [bug] --- *Be clear, concise and precise in your description of the problem. @@ -38,7 +39,7 @@ output by `rubocop -V`, include them as well. Here's an example: ``` $ [bundle exec] rubocop -V -1.73.0 (using Parser 3.3.5.0, rubocop-ast 1.32.3, analyzing as Ruby 3.3, running on ruby 3.3.5) [x86_64-linux] +1.73.2 (using Parser 3.3.5.0, rubocop-ast 1.32.3, analyzing as Ruby 3.3, running on ruby 3.3.5) [x86_64-linux] - rubocop-performance 1.22.1 - rubocop-rspec 3.1.0 ``` diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 961828ae28e5..221ed5f73da8 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,6 +1,7 @@ --- name: Feature Request about: Suggest new RuboCop features or improvements to existing features. +labels: [feature request] --- ## Is your feature request related to a problem? Please describe. diff --git a/.github/workflows/github_release.yml b/.github/workflows/github_release.yml index 74230aa9ca77..2607babd6251 100644 --- a/.github/workflows/github_release.yml +++ b/.github/workflows/github_release.yml @@ -21,5 +21,5 @@ jobs: with: tag: ${{ github.ref_name }} name: RuboCop ${{ github.ref_name }} - bodyFile: releases${{ github.ref_name }}.md + bodyFile: relnotes/${{ github.ref_name }}.md token: ${{ secrets.GITHUB_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cab487aaac9..da0490f2b859 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,32 @@ ## master (unreleased) +## 1.73.2 (2025-03-04) + +### Bug fixes + +* [#13942](https://github.com/rubocop/rubocop/pull/13942): Fix incorrect disabling of departments when inheriting configuration. ([@koic][]) +* [#13766](https://github.com/rubocop/rubocop/issues/13766): Fix false positives for `Style/InverseMethods` when using `any?` or `none?` with safe navigation operator. ([@koic][]) +* [#13938](https://github.com/rubocop/rubocop/pull/13938): Fix false positives for `Style/RedundantCondition` when a variable or a constant is used. ([@koic][]) +* [#13935](https://github.com/rubocop/rubocop/pull/13935): Fix a false negative for `Style/RedundantFreeze` when calling methods that produce frozen objects with numblocks. ([@earlopain][]) +* [#13928](https://github.com/rubocop/rubocop/issues/13928): Fix `end pattern with unmatched parenthesis: / (RegexpError)` on Ruby 3.2.0. ([@dvandersluis][]) +* [#13933](https://github.com/rubocop/rubocop/issues/13933): Fix wrong autocorrect for `Style/KeywordParametersOrder` when the arguments are on multiple lines and contain comments. ([@earlopain][]) + +### Changes + +* [#12669](https://github.com/rubocop/rubocop/issues/12669): Update autocorrection for `Lint/EmptyConditionalBody` to be safe. ([@dvandersluis][]) + +## 1.73.1 (2025-02-27) + +### Bug fixes + +* [#13920](https://github.com/rubocop/rubocop/issues/13920): Fix an error for `Lint/MixedCaseRange` when `/[[ ]]/` is used. ([@koic][]) +* [#13912](https://github.com/rubocop/rubocop/pull/13912): Fix wrong autocorrect for `Lint/EmptyConditionalBody` when assigning to a variable with only a single branch. ([@earlopain][]) +* [#13913](https://github.com/rubocop/rubocop/issues/13913): Fix false positives for `Style/RedundantCondition` when using when true is used as the true branch and the condition is not a predicate method. ([@koic][]) +* [#13909](https://github.com/rubocop/rubocop/issues/13909): Fix false positive with `Layout/ClosingParenthesisIndentation` when first parameter is a hash. ([@tejasbubane][]) +* [#13915](https://github.com/rubocop/rubocop/pull/13915): Fix writing generics type of rbs-inline annotation for nested class in `Style/CommentedKeyword`. ([@dak2][]) +* [#13916](https://github.com/rubocop/rubocop/issues/13916): Fix `Lint/LiteralAsCondition` acting on the right hand side of && nodes. ([@zopolis4][]) + ## 1.73.0 (2025-02-26) ### New features diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 88dea0850803..f503b2ed5239 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,7 +17,7 @@ do so. ```console $ rubocop -V -1.73.0 (using Parser 3.3.5.0, rubocop-ast 1.32.3, analyzing as Ruby 3.3, running on ruby 3.3.5) [x86_64-linux] +1.73.2 (using Parser 3.3.5.0, rubocop-ast 1.32.3, analyzing as Ruby 3.3, running on ruby 3.3.5) [x86_64-linux] - rubocop-performance 1.22.1 - rubocop-rspec 3.1.0 ``` diff --git a/config/default.yml b/config/default.yml index f95515ff9441..37f4be2cb400 100644 --- a/config/default.yml +++ b/config/default.yml @@ -1887,10 +1887,9 @@ Lint/EmptyConditionalBody: Description: 'Checks for the presence of `if`, `elsif` and `unless` branches without a body.' Enabled: true AutoCorrect: contextual - SafeAutoCorrect: false AllowComments: true VersionAdded: '0.89' - VersionChanged: '1.61' + VersionChanged: '1.73' Lint/EmptyEnsure: Description: 'Checks for empty ensure block.' @@ -5108,6 +5107,9 @@ Style/RedundantCondition: Description: 'Checks for unnecessary conditional expressions.' Enabled: true VersionAdded: '0.76' + VersionChanged: '1.73' + AllowedMethods: + - nonzero? Style/RedundantConditional: Description: "Don't return true/false from a conditional." diff --git a/config/internal_affairs.yml b/config/internal_affairs.yml index e3d256539a10..cfff30cf0966 100644 --- a/config/internal_affairs.yml +++ b/config/internal_affairs.yml @@ -14,6 +14,10 @@ InternalAffairs/ExampleDescription: Include: - 'spec/rubocop/cop/**/*.rb' +InternalAffairs/NodeTypeGroup: + Include: + - 'lib/rubocop/cop/**/*.rb' + InternalAffairs/OnSendWithoutOnCSend: Include: - 'lib/rubocop/cop/**/*.rb' diff --git a/docs/modules/ROOT/pages/cops_lint.adoc b/docs/modules/ROOT/pages/cops_lint.adoc index e2c6281202ed..f3a1aa8e9661 100644 --- a/docs/modules/ROOT/pages/cops_lint.adoc +++ b/docs/modules/ROOT/pages/cops_lint.adoc @@ -2257,22 +2257,15 @@ end | Enabled | Yes -| Command-line only (Unsafe) +| Command-line only | 0.89 -| 1.61 +| 1.73 |=== Checks for the presence of `if`, `elsif` and `unless` branches without a body. NOTE: empty `else` branches are handled by `Style/EmptyElse`. -[#safety-lintemptyconditionalbody] -=== 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. - [#examples-lintemptyconditionalbody] === Examples @@ -2302,6 +2295,13 @@ unless condition do_something end +# good +if condition + do_something +elsif other_condition + nil +end + # good if condition do_something @@ -3494,12 +3494,15 @@ if 20 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/docs/modules/ROOT/pages/cops_style.adoc b/docs/modules/ROOT/pages/cops_style.adoc index 7f4364e23e34..6abb5be0741d 100644 --- a/docs/modules/ROOT/pages/cops_style.adoc +++ b/docs/modules/ROOT/pages/cops_style.adoc @@ -12838,7 +12838,7 @@ Checks for usage of the %W() syntax when %w() would do. | Yes | Always | 0.76 -| - +| 1.73 |=== Checks for unnecessary conditional expressions. @@ -12901,6 +12901,26 @@ end a.nil? || a ---- +[#allowedmethods_-__nonzero___-_default_-styleredundantcondition] +==== AllowedMethods: ['nonzero?'] (default) + +[source,ruby] +---- +# good +num.nonzero? ? true : false +---- + +[#configurable-attributes-styleredundantcondition] +=== Configurable attributes + +|=== +| Name | Default value | Configurable values + +| AllowedMethods +| `nonzero?` +| Array +|=== + [#styleredundantconditional] == Style/RedundantConditional diff --git a/docs/modules/ROOT/pages/integration_with_other_tools.adoc b/docs/modules/ROOT/pages/integration_with_other_tools.adoc index 8b360d13cf43..0270a958373a 100644 --- a/docs/modules/ROOT/pages/integration_with_other_tools.adoc +++ b/docs/modules/ROOT/pages/integration_with_other_tools.adoc @@ -125,7 +125,7 @@ following to your `.pre-commit-config.yaml` file: [source,yaml] ---- - repo: https://github.com/rubocop/rubocop - rev: v1.73.0 + rev: v1.73.2 hooks: - id: rubocop ---- @@ -136,7 +136,7 @@ entries in `additional_dependencies`: [source,yaml] ---- - repo: https://github.com/rubocop/rubocop - rev: v1.73.0 + rev: v1.73.2 hooks: - id: rubocop additional_dependencies: diff --git a/lib/rubocop/config_loader.rb b/lib/rubocop/config_loader.rb index 4e0ba50457ec..1aee37210318 100644 --- a/lib/rubocop/config_loader.rb +++ b/lib/rubocop/config_loader.rb @@ -63,7 +63,6 @@ def load_file(file, check: true) loaded_features = resolver.resolve_requires(path, hash) add_loaded_features(loaded_features) - resolver.override_department_setting_for_cops({}, hash) resolver.resolve_inheritance_from_gems(hash) resolver.resolve_inheritance(path, hash, file, debug?) hash.delete('inherit_from') diff --git a/lib/rubocop/cop/internal_affairs.rb b/lib/rubocop/cop/internal_affairs.rb index d9d2ef54ddc4..39bf3b4eb0e9 100644 --- a/lib/rubocop/cop/internal_affairs.rb +++ b/lib/rubocop/cop/internal_affairs.rb @@ -17,6 +17,7 @@ require_relative 'internal_affairs/node_first_or_last_argument' require_relative 'internal_affairs/node_matcher_directive' require_relative 'internal_affairs/node_pattern_groups' +require_relative 'internal_affairs/node_type_group' require_relative 'internal_affairs/node_type_multiple_predicates' require_relative 'internal_affairs/node_type_predicate' require_relative 'internal_affairs/numblock_handler' diff --git a/lib/rubocop/cop/internal_affairs/example_description.rb b/lib/rubocop/cop/internal_affairs/example_description.rb index ca894f72b2f9..ad6e8b21d93b 100644 --- a/lib/rubocop/cop/internal_affairs/example_description.rb +++ b/lib/rubocop/cop/internal_affairs/example_description.rb @@ -50,10 +50,12 @@ class ExampleDescription < Base }.freeze EXPECT_NO_CORRECTIONS_DESCRIPTION_MAPPING = { - /\A(auto[- ]?)?correct/ => 'does not correct' + /\A(auto[- ]?)?corrects?/ => 'does not correct', + /\band (auto[- ]?)?corrects/ => 'but does not correct' }.freeze EXPECT_CORRECTION_DESCRIPTION_MAPPING = { + /\bbut (does not|doesn't) (auto[- ]?)?correct/ => 'and autocorrects', /\b(does not|doesn't) (auto[- ]?)?correct/ => 'autocorrects' }.freeze diff --git a/lib/rubocop/cop/internal_affairs/node_type_group.rb b/lib/rubocop/cop/internal_affairs/node_type_group.rb new file mode 100644 index 000000000000..6fb3a3365bd2 --- /dev/null +++ b/lib/rubocop/cop/internal_affairs/node_type_group.rb @@ -0,0 +1,91 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module InternalAffairs + # Checks that node types are checked against their group when all types of a + # group are checked. + # + # @example + # # bad + # node.type?(:irange, :erange) + # + # # good + # node.range_type? + # + # # bad + # node.type?(:irange, :erange, :send, :csend) + # + # # good + # node.type?(:range, :call) + # + class NodeTypeGroup < Base + extend AutoCorrector + include RangeHelp + + MSG = 'Use `:%s` instead of individually listing group types.' + + RESTRICT_ON_SEND = %i[type? each_ancestor each_child_node each_descendant each_node].freeze + + def on_send(node) + return unless node.receiver + + symbol_args = node.arguments.select(&:sym_type?) + return if symbol_args.none? + + NodePatternGroups::NODE_GROUPS.each do |group_name, group_types| + next unless group_satisfied?(group_types, symbol_args) + + offense_range = arguments_range(node) + add_offense(offense_range, message: format(MSG, group: group_name)) do |corrector| + autocorrect(corrector, node, symbol_args, group_name, group_types) + end + end + end + alias on_csend on_send + + private + + def arguments_range(node) + range_between( + node.first_argument.source_range.begin_pos, + node.last_argument.source_range.end_pos + ) + end + + def group_satisfied?(group_types, symbol_args) + group_types.all? { |type| symbol_args.any? { |arg| arg.value == type } } + end + + def autocorrect(corrector, node, symbol_args, group_name, group_types) + if node.method?(:type?) && node.arguments.count == group_types.count + autocorrect_to_explicit_predicate(corrector, node, group_name) + else + autocorrect_keep_method(corrector, symbol_args, group_name, group_types) + end + end + + def autocorrect_to_explicit_predicate(corrector, node, group_name) + corrector.replace(node.selector, "#{group_name}_type?") + corrector.remove(arguments_range(node)) + end + + def autocorrect_keep_method(corrector, symbol_args, group_name, group_types) + first_replaced = false + symbol_args.each do |arg| + next unless group_types.include?(arg.value) + + if first_replaced + range = range_with_surrounding_space(arg.source_range) + range = range_with_surrounding_comma(range, :left) + corrector.remove(range) + else + first_replaced = true + corrector.replace(arg, ":#{group_name}") + end + end + end + end + end + end +end diff --git a/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb b/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb index 99d4bf043b21..8e7e9e61edfd 100644 --- a/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +++ b/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb @@ -155,10 +155,10 @@ def expected_column(left_paren, elements) end def all_elements_aligned?(elements) - elements.flat_map do |e| - if e.hash_type? - e.each_child_node.map { |child| child.loc.column } - else + if elements.first.hash_type? + elements.first.each_child_node.map { |child| child.loc.column } + else + elements.flat_map do |e| e.loc.column end end.uniq.count == 1 diff --git a/lib/rubocop/cop/lint/empty_conditional_body.rb b/lib/rubocop/cop/lint/empty_conditional_body.rb index 84b937455b33..deca3e118f4d 100644 --- a/lib/rubocop/cop/lint/empty_conditional_body.rb +++ b/lib/rubocop/cop/lint/empty_conditional_body.rb @@ -7,11 +7,6 @@ module Lint # # 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 @@ -41,6 +36,13 @@ module Lint # if condition # do_something # elsif other_condition + # nil + # end + # + # # good + # if condition + # do_something + # elsif other_condition # do_something_else # end # @@ -63,11 +65,9 @@ module Lint class EmptyConditionalBody < Base extend AutoCorrector include CommentsHelp - include RangeHelp MSG = 'Avoid `%s` branches without a body.' - # rubocop:disable Metrics/AbcSize def on_if(node) return if node.body || same_line?(node.loc.begin, node.loc.end) return if cop_config['AllowComments'] && contains_comments?(node) @@ -75,12 +75,11 @@ def on_if(node) range = offense_range(node) add_offense(range, message: format(MSG, keyword: node.keyword)) do |corrector| - next if node.parent&.call_type? + next unless can_simplify_conditional?(node) - autocorrect(corrector, node) + flip_orphaned_else(corrector, node) end end - # rubocop:enable Metrics/AbcSize private @@ -92,53 +91,23 @@ def offense_range(node) end end - def autocorrect(corrector, node) - remove_comments(corrector, node) - remove_empty_branch(corrector, node) - correct_other_branches(corrector, node) - end - - def remove_comments(corrector, node) - comments_in_range(node).each do |comment| - range = range_by_whole_lines(comment.source_range, include_final_newline: true) - corrector.remove(range) - end + def can_simplify_conditional?(node) + node.else_branch && node.loc.else.source == 'else' end - # rubocop:disable Metrics/AbcSize def remove_empty_branch(corrector, node) range = if empty_if_branch?(node) && else_branch?(node) branch_range(node) - elsif same_line?(node, else_kw_loc = node.loc.else) - node.source_range.begin.join(else_kw_loc.begin) - elsif node.parent&.loc.respond_to?(:end) && - same_line?(node, end_loc = node.parent.loc.end) - node.source_range.begin.join(end_loc.begin) else deletion_range(branch_range(node)) end corrector.remove(range) end - # rubocop:enable Metrics/AbcSize - - def correct_other_branches(corrector, node) - return unless require_other_branches_correction?(node) - - if node.else_branch&.if_type? && !node.else_branch.modifier_form? - # Replace an orphaned `elsif` with `if` - corrector.replace(node.else_branch.loc.keyword, 'if') - else - # Flip orphaned `else` - corrector.replace(node.loc.else, "#{node.inverse_keyword} #{node.condition.source}") - end - end - - def require_other_branches_correction?(node) - return false unless node.if_type? && node.else? - return false if !empty_if_branch?(node) && node.elsif? - !empty_elsif_branch?(node) + def flip_orphaned_else(corrector, node) + corrector.replace(node.loc.else, "#{node.inverse_keyword} #{node.condition.source}") + remove_empty_branch(corrector, node) end def empty_if_branch?(node) @@ -149,36 +118,17 @@ def empty_if_branch?(node) if_branch.if_type? && !if_branch.body end - def empty_elsif_branch?(node) - return false unless (else_branch = node.else_branch) - - else_branch.if_type? && !else_branch.body - end - def else_branch?(node) node.else_branch && !node.else_branch.if_type? end - # rubocop:disable Metrics/AbcSize def branch_range(node) if empty_if_branch?(node) && else_branch?(node) node.source_range.with(end_pos: node.loc.else.begin_pos) elsif node.loc.else node.source_range.with(end_pos: node.condition.source_range.end_pos) - elsif all_branches_body_missing?(node) - if_node = node.ancestors.detect(&:if?) - node.source_range.join(if_node.loc.end.end) - else - node.source_range end end - # rubocop:enable Metrics/AbcSize - - def all_branches_body_missing?(node) - return false unless node.parent&.if_type? - - node.parent.branches.compact.empty? - end def deletion_range(range) # Collect a range between the start of the `if` node and the next relevant node, diff --git a/lib/rubocop/cop/lint/erb_new_arguments.rb b/lib/rubocop/cop/lint/erb_new_arguments.rb index 466aea7727b0..1130dfd8b9e8 100644 --- a/lib/rubocop/cop/lint/erb_new_arguments.rb +++ b/lib/rubocop/cop/lint/erb_new_arguments.rb @@ -156,12 +156,6 @@ def override_by_legacy_args(kwargs, node) overridden_kwargs end - - def arguments_range(node) - arguments = node.arguments - - range_between(arguments.first.source_range.begin_pos, arguments.last.source_range.end_pos) - end end end end diff --git a/lib/rubocop/cop/lint/literal_as_condition.rb b/lib/rubocop/cop/lint/literal_as_condition.rb index 1344798053d8..74f5ce7ebd73 100644 --- a/lib/rubocop/cop/lint/literal_as_condition.rb +++ b/lib/rubocop/cop/lint/literal_as_condition.rb @@ -18,12 +18,15 @@ module Lint # 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 # @@ -39,23 +42,13 @@ class LiteralAsCondition < Base MSG = 'Literal `%s` appeared as a condition.' RESTRICT_ON_SEND = [:!].freeze - # rubocop:disable Metrics/AbcSize def on_and(node) - if node.lhs.truthy_literal? && node.rhs.truthy_literal? - add_offense(node) do |corrector| - corrector.replace(node, 'true') - end - elsif node.lhs.truthy_literal? - add_offense(node.lhs) do |corrector| - corrector.replace(node, node.rhs.source) - end - elsif node.rhs.truthy_literal? - add_offense(node.rhs) do |corrector| - corrector.replace(node, node.lhs.source) - end + return unless node.lhs.truthy_literal? + + add_offense(node.lhs) do |corrector| + corrector.replace(node, node.rhs.source) end end - # rubocop:enable Metrics/AbcSize def on_if(node) cond = condition(node) diff --git a/lib/rubocop/cop/lint/mixed_case_range.rb b/lib/rubocop/cop/lint/mixed_case_range.rb index 3a54e2fd5de0..38e121ab1650 100644 --- a/lib/rubocop/cop/lint/mixed_case_range.rb +++ b/lib/rubocop/cop/lint/mixed_case_range.rb @@ -94,7 +94,7 @@ def skip_expression?(expr) def skip_range?(range_start, range_end) [range_start, range_end].any? do |bound| - bound.type != :literal + bound&.type != :literal end end diff --git a/lib/rubocop/cop/mixin/range_help.rb b/lib/rubocop/cop/mixin/range_help.rb index 0fd76674301c..1e447eaa46d8 100644 --- a/lib/rubocop/cop/mixin/range_help.rb +++ b/lib/rubocop/cop/mixin/range_help.rb @@ -34,6 +34,18 @@ def contents_range(node) range_between(node.loc.begin.end_pos, node.loc.end.begin_pos) end + # A range containing the first to the last argument + # of a method call or method definition. + # def foo(a, b:) + # ^^^^^ + # bar(1, 2, 3, &blk) + # ^^^^^^^^^^^^^ + # baz { |x, y:, z:| } + # ^^^^^^^^^ + def arguments_range(node) + node.first_argument.source_range.join(node.last_argument.source_range) + end + def range_between(start_pos, end_pos) Parser::Source::Range.new(processed_source.buffer, start_pos, end_pos) end diff --git a/lib/rubocop/cop/style/commented_keyword.rb b/lib/rubocop/cop/style/commented_keyword.rb index 17ec063d7156..d41702d10ce3 100644 --- a/lib/rubocop/cop/style/commented_keyword.rb +++ b/lib/rubocop/cop/style/commented_keyword.rb @@ -57,7 +57,7 @@ class CommentedKeyword < Base REGEXP = /(?\S+).*#/.freeze - SUBCLASS_DEFINITION = /\A\s*class\s+\w+\s*<\s*\w+/.freeze + SUBCLASS_DEFINITION = /\A\s*class\s+(\w|::)+\s*<\s*(\w|::)+/.freeze METHOD_DEFINITION = /\A\s*def\s/.freeze def on_new_investigation diff --git a/lib/rubocop/cop/style/expand_path_arguments.rb b/lib/rubocop/cop/style/expand_path_arguments.rb index 2f36cd7bd85a..f7617f415c4e 100644 --- a/lib/rubocop/cop/style/expand_path_arguments.rb +++ b/lib/rubocop/cop/style/expand_path_arguments.rb @@ -137,11 +137,11 @@ def autocorrect_expand_path(corrector, current_path, default_dir) case depth(stripped_current_path) when 0 - range = arguments_range(current_path) + range = arguments_range(current_path.parent) corrector.replace(range, '__FILE__') when 1 - range = arguments_range(current_path) + range = arguments_range(current_path.parent) corrector.replace(range, '__dir__') else @@ -185,11 +185,6 @@ def remove_parent_method(corrector, default_dir) corrector.remove(node.loc.dot) corrector.remove(node.loc.selector) end - - def arguments_range(node) - range_between(node.parent.first_argument.source_range.begin_pos, - node.parent.last_argument.source_range.end_pos) - end end end end diff --git a/lib/rubocop/cop/style/inverse_methods.rb b/lib/rubocop/cop/style/inverse_methods.rb index 3a9b8ec60138..01efa22b3be6 100644 --- a/lib/rubocop/cop/style/inverse_methods.rb +++ b/lib/rubocop/cop/style/inverse_methods.rb @@ -46,6 +46,7 @@ class InverseMethods < Base MSG = 'Use `%s` instead of inverting `%s`.' CLASS_COMPARISON_METHODS = %i[<= >= < >].freeze + SAFE_NAVIGATION_INCOMPATIBLE_METHODS = (CLASS_COMPARISON_METHODS + %i[any? none?]).freeze EQUALITY_METHODS = %i[== != =~ !~ <= >= < >].freeze NEGATED_EQUALITY_METHODS = %i[!= !~].freeze CAMEL_CASE = /[A-Z]+[a-z]+/.freeze @@ -77,7 +78,7 @@ def self.autocorrect_incompatible_with def on_send(node) inverse_candidate?(node) do |method_call, lhs, method, rhs| return unless inverse_methods.key?(method) - return if negated?(node) || relational_comparison_with_safe_navigation?(method_call) + return if negated?(node) || safe_navigation_incompatible?(method_call) return if part_of_ignored_node?(node) return if possible_class_hierarchy_check?(lhs, rhs, method) @@ -154,10 +155,6 @@ def negated?(node) node.parent.respond_to?(:method?) && node.parent.method?(:!) end - def relational_comparison_with_safe_navigation?(node) - node.csend_type? && CLASS_COMPARISON_METHODS.include?(node.method_name) - end - def not_to_receiver(node, method_call) node.loc.selector.begin.join(method_call.source_range.begin) end @@ -166,6 +163,12 @@ def end_parentheses(node, method_call) method_call.source_range.end.join(node.source_range.end) end + def safe_navigation_incompatible?(node) + return false unless node.csend_type? + + SAFE_NAVIGATION_INCOMPATIBLE_METHODS.include?(node.method_name) + end + # When comparing classes, `!(Integer < Numeric)` is not the same as # `Integer > Numeric`. def possible_class_hierarchy_check?(lhs, rhs, method) diff --git a/lib/rubocop/cop/style/keyword_parameters_order.rb b/lib/rubocop/cop/style/keyword_parameters_order.rb index dfca953132fc..7f3a7cdd3ea8 100644 --- a/lib/rubocop/cop/style/keyword_parameters_order.rb +++ b/lib/rubocop/cop/style/keyword_parameters_order.rb @@ -42,19 +42,25 @@ def on_kwoptarg(node) return if kwarg_nodes.empty? add_offense(node) do |corrector| - if node.parent.find(&:kwoptarg_type?) == node - corrector.insert_before(node, "#{kwarg_nodes.map(&:source).join(', ')}, ") + defining_node = node.each_ancestor(:def, :defs, :block).first + next if processed_source.contains_comment?(arguments_range(defining_node)) + next unless node.parent.find(&:kwoptarg_type?) == node - arguments = node.each_ancestor(:def, :defs, :block).first.arguments - append_newline_to_last_kwoptarg(arguments, corrector) unless parentheses?(arguments) - - remove_kwargs(kwarg_nodes, corrector) - end + autocorrect(corrector, node, defining_node, kwarg_nodes) end end private + def autocorrect(corrector, node, defining_node, kwarg_nodes) + corrector.insert_before(node, "#{kwarg_nodes.map(&:source).join(', ')}, ") + + arguments = defining_node.arguments + append_newline_to_last_kwoptarg(arguments, corrector) unless parentheses?(arguments) + + remove_kwargs(kwarg_nodes, corrector) + end + def append_newline_to_last_kwoptarg(arguments, corrector) last_argument = arguments.last return if last_argument.type?(:kwrestarg, :blockarg) diff --git a/lib/rubocop/cop/style/multiline_block_chain.rb b/lib/rubocop/cop/style/multiline_block_chain.rb index 393a8673b72a..5fb7ccccc692 100644 --- a/lib/rubocop/cop/style/multiline_block_chain.rb +++ b/lib/rubocop/cop/style/multiline_block_chain.rb @@ -28,7 +28,7 @@ class MultilineBlockChain < Base MSG = 'Avoid multi-line chains of blocks.' def on_block(node) - node.send_node.each_node(:send, :csend) do |send_node| + node.send_node.each_node(:call) do |send_node| receiver = send_node.receiver next unless receiver&.any_block_type? && receiver.multiline? diff --git a/lib/rubocop/cop/style/multiline_method_signature.rb b/lib/rubocop/cop/style/multiline_method_signature.rb index b345944b33ce..523544acdc00 100644 --- a/lib/rubocop/cop/style/multiline_method_signature.rb +++ b/lib/rubocop/cop/style/multiline_method_signature.rb @@ -50,7 +50,7 @@ def autocorrect(corrector, node, begin_of_arguments) corrector.remove(range_by_whole_lines(arguments.loc.end, include_final_newline: true)) end - arguments_range = arguments_range(node) + arguments_range = range_with_surrounding_space(arguments_range(node), side: :left) # If the method name isn't on the same line as def, move it directly after def if arguments_range.first_line != opening_line(node) corrector.remove(node.loc.name) @@ -66,14 +66,6 @@ def last_line_source_of_arguments(arguments) processed_source[arguments.last_line - 1].strip end - def arguments_range(node) - range = range_between( - node.first_argument.source_range.begin_pos, node.last_argument.source_range.end_pos - ) - - range_with_surrounding_space(range, side: :left) - end - def opening_line(node) node.first_line end diff --git a/lib/rubocop/cop/style/redundant_condition.rb b/lib/rubocop/cop/style/redundant_condition.rb index 6bb7463c074b..ec82d87007c6 100644 --- a/lib/rubocop/cop/style/redundant_condition.rb +++ b/lib/rubocop/cop/style/redundant_condition.rb @@ -58,7 +58,12 @@ module Style # # good # a.nil? || a # + # @example AllowedMethods: ['nonzero?'] (default) + # # good + # num.nonzero? ? true : false + # class RedundantCondition < Base + include AllowedMethods include CommentsHelp include RangeHelp extend AutoCorrector @@ -172,11 +177,17 @@ def synonymous_condition_and_branch?(node) !use_hash_key_access?(if_branch) end + # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity def if_branch_is_true_type_and_else_is_not?(node) return false unless node.ternary? || node.if? + cond = node.condition + return false unless cond.call_type? + return false if !cond.predicate_method? || allowed_method?(cond.method_name) + node.if_branch&.true_type? && node.else_branch && !node.else_branch.true_type? end + # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity def branches_have_assignment?(node) _condition, if_branch, else_branch = *node # rubocop:disable InternalAffairs/NodeDestructuring diff --git a/lib/rubocop/cop/style/redundant_freeze.rb b/lib/rubocop/cop/style/redundant_freeze.rb index 8d5beb29c187..0503270bacbb 100644 --- a/lib/rubocop/cop/style/redundant_freeze.rb +++ b/lib/rubocop/cop/style/redundant_freeze.rb @@ -60,7 +60,7 @@ def strip_parenthesis(node) (begin (send !{(str _) array} {:+ :- :* :** :/ :%} {float int})) (begin (send _ {:== :=== :!= :<= :>= :< :>} _)) (send _ {:count :length :size} ...) - (block (send _ {:count :length :size} ...) ...) + (any_block (send _ {:count :length :size} ...) ...) } PATTERN end diff --git a/lib/rubocop/cop/style/sole_nested_conditional.rb b/lib/rubocop/cop/style/sole_nested_conditional.rb index 8018d29495aa..c7f5bacd1809 100644 --- a/lib/rubocop/cop/style/sole_nested_conditional.rb +++ b/lib/rubocop/cop/style/sole_nested_conditional.rb @@ -230,12 +230,6 @@ def require_parentheses?(condition) !condition.comparison_method? end - def arguments_range(node) - range_between( - node.first_argument.source_range.begin_pos, node.last_argument.source_range.end_pos - ) - end - def wrap_condition?(node) node.operator_keyword? || (node.call_type? && node.arguments.any? && !node.parenthesized?) end diff --git a/lib/rubocop/cop/utils/format_string.rb b/lib/rubocop/cop/utils/format_string.rb index decdfec60ab7..aa46e70ac002 100644 --- a/lib/rubocop/cop/utils/format_string.rb +++ b/lib/rubocop/cop/utils/format_string.rb @@ -5,8 +5,11 @@ module Cop module Utils # Parses {Kernel#sprintf} format strings. class FormatString + # Escaping the `#` in `INTERPOLATION` and `TEMPLATE_NAME` is necessary to + # avoid a bug in Ruby 3.2.0 + # See: https://bugs.ruby-lang.org/issues/19379 DIGIT_DOLLAR = /(?\d+)\$/.freeze - INTERPOLATION = /#\{.*?\}/.freeze + INTERPOLATION = /\#\{.*?\}/.freeze FLAG = /[ #0+-]|#{DIGIT_DOLLAR}/.freeze NUMBER_ARG = /\*#{DIGIT_DOLLAR}?/.freeze NUMBER = /\d+|#{NUMBER_ARG}|#{INTERPOLATION}/.freeze @@ -14,7 +17,7 @@ class FormatString PRECISION = /\.(?#{NUMBER}?)/.freeze TYPE = /(?[bBdiouxXeEfgGaAcps])/.freeze NAME = /<(?\w+)>/.freeze - TEMPLATE_NAME = /(?\w+)\}/.freeze + TEMPLATE_NAME = /(?\w+)\}/.freeze SEQUENCE = / % (?%) diff --git a/lib/rubocop/version.rb b/lib/rubocop/version.rb index d8ea91610df8..fa04e384ff0b 100644 --- a/lib/rubocop/version.rb +++ b/lib/rubocop/version.rb @@ -3,7 +3,7 @@ module RuboCop # This module holds the RuboCop version information. module Version - STRING = '1.73.0' + STRING = '1.73.2' MSG = '%s (using %s, ' \ 'rubocop-ast %s, ' \ diff --git a/relnotes/v1.73.1.md b/relnotes/v1.73.1.md new file mode 100644 index 000000000000..63933e355b16 --- /dev/null +++ b/relnotes/v1.73.1.md @@ -0,0 +1,14 @@ +### Bug fixes + +* [#13920](https://github.com/rubocop/rubocop/issues/13920): Fix an error for `Lint/MixedCaseRange` when `/[[ ]]/` is used. ([@koic][]) +* [#13912](https://github.com/rubocop/rubocop/pull/13912): Fix wrong autocorrect for `Lint/EmptyConditionalBody` when assigning to a variable with only a single branch. ([@earlopain][]) +* [#13913](https://github.com/rubocop/rubocop/issues/13913): Fix false positives for `Style/RedundantCondition` when using when true is used as the true branch and the condition is not a predicate method. ([@koic][]) +* [#13909](https://github.com/rubocop/rubocop/issues/13909): Fix false positive with `Layout/ClosingParenthesisIndentation` when first parameter is a hash. ([@tejasbubane][]) +* [#13915](https://github.com/rubocop/rubocop/pull/13915): Fix writing generics type of rbs-inline annotation for nested class in `Style/CommentedKeyword`. ([@dak2][]) +* [#13916](https://github.com/rubocop/rubocop/issues/13916): Fix `Lint/LiteralAsCondition` acting on the right hand side of && nodes. ([@zopolis4][]) + +[@koic]: https://github.com/koic +[@earlopain]: https://github.com/earlopain +[@tejasbubane]: https://github.com/tejasbubane +[@dak2]: https://github.com/dak2 +[@zopolis4]: https://github.com/zopolis4 diff --git a/relnotes/v1.73.2.md b/relnotes/v1.73.2.md new file mode 100644 index 000000000000..e8804065d11b --- /dev/null +++ b/relnotes/v1.73.2.md @@ -0,0 +1,16 @@ +### Bug fixes + +* [#13942](https://github.com/rubocop/rubocop/pull/13942): Fix incorrect disabling of departments when inheriting configuration. ([@koic][]) +* [#13766](https://github.com/rubocop/rubocop/issues/13766): Fix false positives for `Style/InverseMethods` when using `any?` or `none?` with safe navigation operator. ([@koic][]) +* [#13938](https://github.com/rubocop/rubocop/pull/13938): Fix false positives for `Style/RedundantCondition` when a variable or a constant is used. ([@koic][]) +* [#13935](https://github.com/rubocop/rubocop/pull/13935): Fix a false negative for `Style/RedundantFreeze` when calling methods that produce frozen objects with numblocks. ([@earlopain][]) +* [#13928](https://github.com/rubocop/rubocop/issues/13928): Fix `end pattern with unmatched parenthesis: / (RegexpError)` on Ruby 3.2.0. ([@dvandersluis][]) +* [#13933](https://github.com/rubocop/rubocop/issues/13933): Fix wrong autocorrect for `Style/KeywordParametersOrder` when the arguments are on multiple lines and contain comments. ([@earlopain][]) + +### Changes + +* [#12669](https://github.com/rubocop/rubocop/issues/12669): Update autocorrection for `Lint/EmptyConditionalBody` to be safe. ([@dvandersluis][]) + +[@koic]: https://github.com/koic +[@earlopain]: https://github.com/earlopain +[@dvandersluis]: https://github.com/dvandersluis diff --git a/spec/rubocop/cli_spec.rb b/spec/rubocop/cli_spec.rb index 9e571a01aa51..665eb3b74219 100644 --- a/spec/rubocop/cli_spec.rb +++ b/spec/rubocop/cli_spec.rb @@ -2285,8 +2285,7 @@ def method(foo, bar, qux, fred, arg5, f) end #{'#' * 85} create_file('example.rb', <<~RUBY) # frozen_string_literal: true - if foo and bar - end + a.foo..b.bar # Lint/AmbiguousRange offense is not safe correctable RUBY expect(cli.run(['--display-only-safe-correctable', 'example.rb'])).to eq(0) expect($stderr.string).to eq('') diff --git a/spec/rubocop/config_loader_spec.rb b/spec/rubocop/config_loader_spec.rb index b90e192d06aa..f299858c46ec 100644 --- a/spec/rubocop/config_loader_spec.rb +++ b/spec/rubocop/config_loader_spec.rb @@ -804,6 +804,12 @@ it "handles EnabledByDefault: #{enabled_by_default}, " \ "DisabledByDefault: #{disabled_by_default} with disabled #{custom_dept_to_disable}" do create_file('grandparent_rubocop.yml', <<~YAML) + Layout: + Enabled: false + + Layout/EndOfLine: + Enabled: true + Naming/FileName: Enabled: pending @@ -841,6 +847,12 @@ EnabledByDefault: #{enabled_by_default} DisabledByDefault: #{disabled_by_default} + Layout: + Enabled: false + + Layout/LineLength: + Enabled: true + Style: Enabled: false @@ -870,6 +882,15 @@ def enabled?(cop) next end + # Department disabled in grandparent config. + expect(enabled?('Layout/DotPosition')).to be(false) + + # Enabled in grandparent config, disabled in user config. + expect(enabled?('Layout/EndOfLine')).to be(false) + + # Department disabled in grandparent config, cop enabled in user config. + expect(enabled?('Layout/LineLength')).to be(true) + # Department disabled in parent config, cop enabled in child. expect(enabled?('Metrics/MethodLength')).to be(true) diff --git a/spec/rubocop/cop/internal_affairs/example_description_spec.rb b/spec/rubocop/cop/internal_affairs/example_description_spec.rb index 22a95a9d9f60..318d89839248 100644 --- a/spec/rubocop/cop/internal_affairs/example_description_spec.rb +++ b/spec/rubocop/cop/internal_affairs/example_description_spec.rb @@ -203,6 +203,12 @@ expect_correction('code', source: 'new code') end RUBY + + expect_correction(<<~RUBY) + it 'autocorrects' do + expect_correction('code', source: 'new code') + end + RUBY end context 'in conjunction with expect_offense' do @@ -214,6 +220,13 @@ expect_correction('code') end RUBY + + expect_correction(<<~RUBY) + it 'registers an offense and autocorrects' do + expect_offense('code') + expect_correction('code') + end + RUBY end context 'when the description is invalid for both methods' do @@ -225,6 +238,13 @@ expect_correction('code') end RUBY + + expect_correction(<<~RUBY) + it 'registers an offense and autocorrects' do + expect_offense('code') + expect_correction('code') + end + RUBY end end end @@ -238,6 +258,12 @@ expect_no_corrections end RUBY + + expect_correction(<<~RUBY) + it 'does not correct' do + expect_no_corrections + end + RUBY end context 'in conjunction with expect_offense' do @@ -249,8 +275,45 @@ expect_no_corrections end RUBY + + expect_correction(<<~RUBY) + it 'does not correct' do + expect_offense('code') + expect_no_corrections + end + RUBY end end + + it 'registers an offense with a "registers an offense and corrects" description' do + expect_offense(<<~RUBY) + it 'registers an offense and corrects' do + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Description does not match use of `expect_no_corrections`. + expect_no_corrections + end + RUBY + + expect_correction(<<~RUBY) + it 'registers an offense but does not correct' do + expect_no_corrections + end + RUBY + end + + it 'registers an offense with a "registers an offense and autocorrects" description' do + expect_offense(<<~RUBY) + it 'registers an offense and autocorrects' do + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Description does not match use of `expect_no_corrections`. + expect_no_corrections + end + RUBY + + expect_correction(<<~RUBY) + it 'registers an offense but does not correct' do + expect_no_corrections + end + RUBY + end end context 'when not making an expectation on offenses' do diff --git a/spec/rubocop/cop/internal_affairs/node_type_group_spec.rb b/spec/rubocop/cop/internal_affairs/node_type_group_spec.rb new file mode 100644 index 000000000000..1bd61c88c6a3 --- /dev/null +++ b/spec/rubocop/cop/internal_affairs/node_type_group_spec.rb @@ -0,0 +1,126 @@ +# frozen_string_literal: true + +RSpec.describe RuboCop::Cop::InternalAffairs::NodeTypeGroup, :config do + it 'registers an offense when using `type?` with entire `numeric` group' do + expect_offense(<<~RUBY) + node.type?(:int, :float, :rational, :complex) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `:numeric` instead of individually listing group types. + RUBY + + expect_correction(<<~RUBY) + node.numeric_type?() + RUBY + end + + it 'registers an offense when using `type?` with entire `numeric` group in any order' do + expect_offense(<<~RUBY) + node.type?(:rational, :complex, :int, :float) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `:numeric` instead of individually listing group types. + RUBY + + expect_correction(<<~RUBY) + node.numeric_type?() + RUBY + end + + it 'registers an offense when using `type?` with entire `numeric` when other types are present' do + expect_offense(<<~RUBY) + node.type?(:rational, :complex, :send, :int, :float, :def) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `:numeric` instead of individually listing group types. + RUBY + + expect_correction(<<~RUBY) + node.type?(:numeric, :send, :def) + RUBY + end + + it 'registers an offense when using `type?` with multiple groups' do + expect_offense(<<~RUBY) + node.type?(:rational, :complex, :irange, :int, :float, :erange) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `:numeric` instead of individually listing group types. + RUBY + + expect_correction(<<~RUBY) + node.type?(:numeric, :range) + RUBY + end + + it 'registers an offense when using `type?` and other node types as argument' do + expect_offense(<<~RUBY) + node.type?(bar, :irange, foo, :erange) + ^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `:range` instead of individually listing group types. + RUBY + + expect_correction(<<~RUBY) + node.type?(bar, :range, foo) + RUBY + end + + it 'registers an offense for save navigation' do + expect_offense(<<~RUBY) + node&.type?(:irange, :erange) + ^^^^^^^^^^^^^^^^ Use `:range` instead of individually listing group types. + RUBY + + expect_correction(<<~RUBY) + node&.range_type?() + RUBY + end + + it 'registers an offense when chained to a method call' do + expect_offense(<<~RUBY) + node.each_child_node(:irange, :erange).any? + ^^^^^^^^^^^^^^^^ Use `:range` instead of individually listing group types. + RUBY + + expect_correction(<<~RUBY) + node.each_child_node(:range).any? + RUBY + end + + it 'registers an offense when chained to a method call with block' do + expect_offense(<<~RUBY) + node.each_child_node(:irange, :erange).any? do |node| + ^^^^^^^^^^^^^^^^ Use `:range` instead of individually listing group types. + foo?(node) + end + RUBY + + expect_correction(<<~RUBY) + node.each_child_node(:range).any? do |node| + foo?(node) + end + RUBY + end + + it 'registers no offense when the group is incomplete' do + expect_no_offenses(<<~RUBY) + node.type?(:int, :float, :complex) + RUBY + end + + it 'registers no offense with no arguments' do + expect_no_offenses(<<~RUBY) + node.type? + RUBY + end + + it 'registers no offense when there is no receiver' do + expect_no_offenses(<<~RUBY) + type?(:irange, :erange) + RUBY + end + + %i[each_ancestor each_child_node each_descendant each_node].each do |method| + it "registers an offense for #{method}" do + expect_offense(<<~RUBY, method: method) + node.#{method}(:irange, :erange) + _{method} ^^^^^^^^^^^^^^^^ Use `:range` instead of individually listing group types. + RUBY + + expect_correction(<<~RUBY) + node.#{method}(:range) + RUBY + end + end +end diff --git a/spec/rubocop/cop/layout/closing_parenthesis_indentation_spec.rb b/spec/rubocop/cop/layout/closing_parenthesis_indentation_spec.rb index 3409ae7033aa..111491f53566 100644 --- a/spec/rubocop/cop/layout/closing_parenthesis_indentation_spec.rb +++ b/spec/rubocop/cop/layout/closing_parenthesis_indentation_spec.rb @@ -114,6 +114,15 @@ ) RUBY end + + it 'does not register offense for aligned parens when first parameter is a hash' do + expect_no_offenses(<<~RUBY) + some_method({ foo: 1, bar: 2 }, + x: 1, + y: 2 + ) + RUBY + end end context 'without arguments' do diff --git a/spec/rubocop/cop/lint/deprecated_constants_spec.rb b/spec/rubocop/cop/lint/deprecated_constants_spec.rb index 578463db7c9d..92ec7424903a 100644 --- a/spec/rubocop/cop/lint/deprecated_constants_spec.rb +++ b/spec/rubocop/cop/lint/deprecated_constants_spec.rb @@ -184,7 +184,7 @@ RUBY end - it 'registers and corrects an offense when using deprecated methods that have no alternative' do + it 'registers an offense when using deprecated methods that have no alternative' do expect_offense(<<~RUBY) Have::No::Alternative ^^^^^^^^^^^^^^^^^^^^^ Do not use `Have::No::Alternative`, deprecated since Ruby 2.4. diff --git a/spec/rubocop/cop/lint/empty_conditional_body_spec.rb b/spec/rubocop/cop/lint/empty_conditional_body_spec.rb index acfaa3c8ab4d..38f5da374747 100644 --- a/spec/rubocop/cop/lint/empty_conditional_body_spec.rb +++ b/spec/rubocop/cop/lint/empty_conditional_body_spec.rb @@ -10,7 +10,7 @@ end RUBY - expect_correction('') + expect_no_corrections end it 'registers an offense for missing `if` and `else` body' do @@ -21,7 +21,7 @@ end RUBY - expect_correction('') + expect_no_corrections end it 'registers an offense for missing `if` and `else` body with some indentation' do @@ -34,13 +34,10 @@ def foo end RUBY - expect_correction(<<~RUBY) - def foo - end - RUBY + expect_no_corrections end - it 'registers an offense for missing `if` body with present `else` body' do + it 'registers an offense and corrects for missing `if` body with present `else` body' do expect_offense(<<~RUBY) class Foo if condition @@ -123,13 +120,7 @@ class Foo end RUBY - expect_correction(<<~RUBY) - if condition - do_something1 - elsif other_condition2 - do_something2 - end - RUBY + expect_no_corrections end it 'registers an offense for missing `if` and `elsif` body' do @@ -143,11 +134,7 @@ class Foo end RUBY - expect_correction(<<~RUBY) - if other_condition2 - do_something - end - RUBY + expect_no_corrections end it 'registers an offense for missing all branches of `if` and `elsif` body' do @@ -159,7 +146,7 @@ class Foo end RUBY - expect_correction('') + expect_no_corrections end it 'registers an offense for missing all branches of `if` and multiple `elsif` body' do @@ -173,10 +160,10 @@ class Foo end RUBY - expect_correction('') + expect_no_corrections end - it 'registers an offense for missing `if` body with `else`' do + it 'registers an offense and corrects for missing `if` body with `else`' do expect_offense(<<~RUBY) if condition ^^^^^^^^^^^^ Avoid `if` branches without a body. @@ -192,7 +179,7 @@ class Foo RUBY end - it 'registers an offense for missing `unless` body with `else`' do + it 'registers an offense and corrects for missing `unless` body with `else`' do expect_offense(<<~RUBY) unless condition ^^^^^^^^^^^^^^^^ Avoid `unless` branches without a body. @@ -208,7 +195,7 @@ class Foo RUBY end - it 'registers an offense for missing `if` body with conditional `else` body' do + it 'registers an offense and corrects for missing `if` body with conditional `else` body' do expect_offense(<<~RUBY) if condition ^^^^^^^^^^^^ Avoid `if` branches without a body. @@ -224,7 +211,7 @@ class Foo RUBY end - it 'registers an offense for missing `unless` body with conditional `else` body' do + it 'registers an offense and corrects for missing `unless` body with conditional `else` body' do expect_offense(<<~RUBY) unless condition ^^^^^^^^^^^^^^^^ Avoid `unless` branches without a body. @@ -248,7 +235,7 @@ class Foo end RUBY - expect_correction('') + expect_no_corrections end it 'registers an offense for missing `if` body with `elsif`' do @@ -262,13 +249,7 @@ class Foo end RUBY - expect_correction(<<~RUBY) - if other_condition - do_something - elsif another_condition - do_something_else - end - RUBY + expect_no_corrections end it 'registers an offense for missing `elsif` body with `end` on the same line' do @@ -279,11 +260,7 @@ class Foo ^^^^^^^^^^^^^ Avoid `elsif` branches without a body. RUBY - expect_correction(<<~RUBY) - if cond_a - do_a - end - RUBY + expect_no_corrections end it 'registers an offense for missing `elsif` and `else` bodies with `end` on the same line' do @@ -294,11 +271,7 @@ class Foo ^^^^^^^^^^^^^ Avoid `elsif` branches without a body. RUBY - expect_correction(<<~RUBY) - if cond_a - do_a - else;end - RUBY + expect_no_corrections end it 'does not register an offense for missing `elsif` body with a comment' do @@ -322,13 +295,7 @@ class Foo end RUBY - expect_correction(<<~RUBY) - if condition - do_something - else - # noop - end - RUBY + expect_no_corrections end it 'does not register an offense for missing `elsif` body with an inline comment' do @@ -353,13 +320,7 @@ class Foo end RUBY - expect_correction(<<~RUBY) - if foo - do_foo - elsif bar - do_bar - end - RUBY + expect_no_corrections end it 'registers an offense for missing `unless` body' do @@ -369,7 +330,7 @@ class Foo end RUBY - expect_correction('') + expect_no_corrections end it 'registers an offense when missing `if` body and using method call for return value' do @@ -400,7 +361,7 @@ class Foo RUBY end - it 'autocorrects properly when the if is assigned to a variable' do + it 'registers an offense when the if is assigned to a variable' do expect_offense(<<~RUBY) x = if foo ^^^^^^ Avoid `if` branches without a body. @@ -409,9 +370,51 @@ class Foo end RUBY - expect_correction(<<~RUBY) - x = if bar - 5 + expect_no_corrections + end + + it 'registers an offense when there is only one branch with assignment' do + expect_offense(<<~RUBY) + x = if foo + ^^^^^^ Avoid `if` branches without a body. + end + RUBY + + expect_no_corrections + end + + it 'registers an offense when there is only one branch after `||`' do + expect_offense(<<~RUBY) + x || if foo + ^^^^^^ Avoid `if` branches without a body. + end + RUBY + + expect_no_corrections + end + + it 'does not register an offense for an `if` with `nil` body' do + expect_no_offenses(<<~RUBY) + if condition + nil + end + RUBY + end + + it 'does not register an offense for an `unless` with `nil` body' do + expect_no_offenses(<<~RUBY) + unless condition + nil + end + RUBY + end + + it 'does not register an offense for an `elsif` with `nil` body' do + expect_no_offenses(<<~RUBY) + if condition + do_something + elsif other_condition + nil end RUBY end @@ -426,10 +429,42 @@ class Foo condition || other_condition end RUBY + + expect_no_corrections + end + end + + context 'when `AllowComments` is `true`' do + let(:cop_config) { { 'AllowComments' => true } } + + it 'does not register an offense for missing `if` body with a comment' do + expect_no_offenses(<<~RUBY) + if condition + # noop + end + RUBY + end + + it 'does not register an offense for missing `elsif` body with a comment' do + expect_no_offenses(<<~RUBY) + if condition + do_something + elsif other_condition + # noop + end + RUBY + end + + it 'does not register an offense for missing `unless` body with a comment' do + expect_no_offenses(<<~RUBY) + unless condition + # noop + end + RUBY end end - context 'when AllowComments is false' do + context 'when `AllowComments` is `false`' do let(:cop_config) { { 'AllowComments' => false } } it 'registers an offense for missing `if` body with a comment' do @@ -440,7 +475,7 @@ class Foo end RUBY - expect_correction('') + expect_no_corrections end it 'registers an offense for missing `elsif` body with a comment' do @@ -453,11 +488,7 @@ class Foo end RUBY - expect_correction(<<~RUBY) - if condition - do_something - end - RUBY + expect_no_corrections end it 'registers an offense for missing `unless` body with a comment' do @@ -468,7 +499,7 @@ class Foo end RUBY - expect_correction('') + expect_no_corrections end end end diff --git a/spec/rubocop/cop/lint/ensure_return_spec.rb b/spec/rubocop/cop/lint/ensure_return_spec.rb index 58476d95ce35..e691aa363e71 100644 --- a/spec/rubocop/cop/lint/ensure_return_spec.rb +++ b/spec/rubocop/cop/lint/ensure_return_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true RSpec.describe RuboCop::Cop::Lint::EnsureReturn, :config do - it 'registers an offense and corrects for return in ensure' do + it 'registers an offense but does not correct for return in ensure' do expect_offense(<<~RUBY) begin something @@ -15,7 +15,7 @@ expect_no_corrections end - it 'registers an offense and corrects for return with argument in ensure' do + it 'registers an offense but does not correct for return with argument in ensure' do expect_offense(<<~RUBY) begin foo diff --git a/spec/rubocop/cop/lint/literal_as_condition_spec.rb b/spec/rubocop/cop/lint/literal_as_condition_spec.rb index 1869221d37d8..e30da217241c 100644 --- a/spec/rubocop/cop/lint/literal_as_condition_spec.rb +++ b/spec/rubocop/cop/lint/literal_as_condition_spec.rb @@ -177,10 +177,10 @@ end end - it "registers an offense for truthy literal #{lit} in &&" do + it "registers an offense for truthy literal #{lit} on the lhs of &&" do expect_offense(<<~RUBY, lit: lit) - if x && %{lit} - ^{lit} Literal `#{lit}` appeared as a condition. + if %{lit} && x + ^{lit} Literal `#{lit}` appeared as a condition. top end RUBY @@ -192,47 +192,53 @@ RUBY end - it "registers an offense for truthy literal #{lit} in complex cond" do + it "registers an offense for truthy literal #{lit} on the lhs of && with a truthy literal rhs" do expect_offense(<<~RUBY, lit: lit) - if x && !(a && %{lit}) && y && z - ^{lit} Literal `#{lit}` appeared as a condition. + if %{lit} && true + ^{lit} Literal `#{lit}` appeared as a condition. top end RUBY expect_correction(<<~RUBY) - if x && !(a) && y && z - top - end + top RUBY end - it "registers an offense for literal #{lit} in !" do - expect_offense(<<~RUBY, lit: lit) - if !%{lit} - ^{lit} Literal `#{lit}` appeared as a condition. + it "does not register an offense for truthy literal #{lit} on the rhs of &&" do + expect_no_offenses(<<~RUBY) + if x && %{lit} top end RUBY - - expect_no_corrections end - it "registers an offense for truthy literal #{lit} in complex !" do + it "registers an offense for truthy literal #{lit} in complex cond" do expect_offense(<<~RUBY, lit: lit) - if !(x && (y && %{lit})) - ^{lit} Literal `#{lit}` appeared as a condition. + if x && !(%{lit} && a) && y && z + ^{lit} Literal `#{lit}` appeared as a condition. top end RUBY expect_correction(<<~RUBY) - if !(x && (y)) + if x && !(a) && y && z top end RUBY end + it "registers an offense for literal #{lit} in !" do + expect_offense(<<~RUBY, lit: lit) + if !%{lit} + ^{lit} Literal `#{lit}` appeared as a condition. + top + end + RUBY + + expect_no_corrections + end + it "accepts literal #{lit} if it's not an and/or operand" do expect_no_offenses(<<~RUBY) if test(#{lit}) diff --git a/spec/rubocop/cop/lint/mixed_case_range_spec.rb b/spec/rubocop/cop/lint/mixed_case_range_spec.rb index 08cff437709c..e05394a7ef58 100644 --- a/spec/rubocop/cop/lint/mixed_case_range_spec.rb +++ b/spec/rubocop/cop/lint/mixed_case_range_spec.rb @@ -227,6 +227,12 @@ RUBY end + it 'does not register an offense with `/[[ ]]/' do + expect_no_offenses(<<~RUBY) + /[[ ]]/ + RUBY + end + it 'does not register an offense when a character between `Z` and `a` is at the start of range.' do expect_no_offenses(<<~RUBY) foo = /[_-a]/ diff --git a/spec/rubocop/cop/lint/unified_integer_spec.rb b/spec/rubocop/cop/lint/unified_integer_spec.rb index b126a0eb940e..5c2376b5a081 100644 --- a/spec/rubocop/cop/lint/unified_integer_spec.rb +++ b/spec/rubocop/cop/lint/unified_integer_spec.rb @@ -5,7 +5,7 @@ context 'target ruby version < 2.4', :ruby23, unsupported_on: :prism do context "when #{klass}" do context 'without any decorations' do - it 'registers an offense and autocorrects' do + it 'registers an offense but does not correct' do expect_offense(<<~RUBY, klass: klass) 1.is_a?(%{klass}) ^{klass} Use `Integer` instead of `#{klass}`. diff --git a/spec/rubocop/cop/lint/useless_times_spec.rb b/spec/rubocop/cop/lint/useless_times_spec.rb index 0ae4118d38b1..f48d0e2ed5a9 100644 --- a/spec/rubocop/cop/lint/useless_times_spec.rb +++ b/spec/rubocop/cop/lint/useless_times_spec.rb @@ -59,7 +59,7 @@ RUBY end - it 'registers an offense and corrects with 1.times with method chain' do + it 'registers an offense but does not correct with 1.times with method chain' do expect_offense(<<~RUBY) 1.times.reverse_each do ^^^^^^^ Useless call to `1.times` detected. diff --git a/spec/rubocop/cop/style/access_modifier_declarations_spec.rb b/spec/rubocop/cop/style/access_modifier_declarations_spec.rb index 45b4db33d209..6340a4dc312e 100644 --- a/spec/rubocop/cop/style/access_modifier_declarations_spec.rb +++ b/spec/rubocop/cop/style/access_modifier_declarations_spec.rb @@ -161,7 +161,7 @@ def quux; end RUBY end - it 'registers an offense and autocorrects when not all methods are defined in class' do + it 'registers an offense but does not correct when not all methods are defined in class' do expect_offense(<<~RUBY, access_modifier: access_modifier) module Foo def bar; end diff --git a/spec/rubocop/cop/style/case_equality_spec.rb b/spec/rubocop/cop/style/case_equality_spec.rb index 39960f809b74..9cedce6a42fe 100644 --- a/spec/rubocop/cop/style/case_equality_spec.rb +++ b/spec/rubocop/cop/style/case_equality_spec.rb @@ -14,7 +14,7 @@ RUBY end - it 'registers an offense and corrects for === when the receiver is a regexp' do + it 'registers an offense but does not correct for === when the receiver is a regexp' do expect_offense(<<~RUBY) /OMG/ === var ^^^ Avoid the use of the case equality operator `===`. @@ -100,7 +100,7 @@ RUBY end - it 'registers an offense and corrects for === when the receiver is self.klass' do + it 'registers an offense but does not correct for === when the receiver is self.klass' do expect_offense(<<~RUBY) self.klass === var ^^^ Avoid the use of the case equality operator `===`. diff --git a/spec/rubocop/cop/style/commented_keyword_spec.rb b/spec/rubocop/cop/style/commented_keyword_spec.rb index c1b95f780330..5c02fcb5201b 100644 --- a/spec/rubocop/cop/style/commented_keyword_spec.rb +++ b/spec/rubocop/cop/style/commented_keyword_spec.rb @@ -241,6 +241,14 @@ def x(y: "#value") expect_no_offenses(<<~RUBY) class X < Y #[String] end + class A < B::C #[String] + end + class A < B::C::D #[String] + end + class A::B < C #[String] + end + class A::B::C < D #[String] + end RUBY end @@ -261,6 +269,18 @@ class X < Y # String] class X < Y #String ] ^^^^^^^^^ Do not place comments on the same line as the `class` keyword. end + class A < B::C #String] + ^^^^^^^^ Do not place comments on the same line as the `class` keyword. + end + class A < B::C::D #String] + ^^^^^^^^ Do not place comments on the same line as the `class` keyword. + end + class A::B < C #String] + ^^^^^^^^ Do not place comments on the same line as the `class` keyword. + end + class A::B::C < D #String] + ^^^^^^^^ Do not place comments on the same line as the `class` keyword. + end RUBY expect_correction(<<~RUBY) @@ -279,6 +299,18 @@ class X < Y #String ] class X < Y end + #String] + class A < B::C + end + #String] + class A < B::C::D + end + #String] + class A::B < C + end + #String] + class A::B::C < D + end RUBY end diff --git a/spec/rubocop/cop/style/comparable_clamp_spec.rb b/spec/rubocop/cop/style/comparable_clamp_spec.rb index 607627d29fbf..a9b048e4a892 100644 --- a/spec/rubocop/cop/style/comparable_clamp_spec.rb +++ b/spec/rubocop/cop/style/comparable_clamp_spec.rb @@ -227,7 +227,7 @@ expect_no_corrections end - it 'registers and corrects an offense when using `[[low, high].min].max`' do + it 'registers but does not correct an offense when using `[[low, high].min].max`' do expect_offense(<<~RUBY) [low, [x, high].min].max ^^^^^^^^^^^^^^^^^^^^^^^^ Use `Comparable#clamp` instead. @@ -236,7 +236,7 @@ expect_no_corrections end - it 'registers and corrects an offense when using `[low, [high, x].min].max`' do + it 'registers but does not correct an offense when using `[low, [high, x].min].max`' do expect_offense(<<~RUBY) [low, [high, x].min].max ^^^^^^^^^^^^^^^^^^^^^^^^ Use `Comparable#clamp` instead. diff --git a/spec/rubocop/cop/style/identical_conditional_branches_spec.rb b/spec/rubocop/cop/style/identical_conditional_branches_spec.rb index 4dfc2d9f8ef0..e56fa6afdcf7 100644 --- a/spec/rubocop/cop/style/identical_conditional_branches_spec.rb +++ b/spec/rubocop/cop/style/identical_conditional_branches_spec.rb @@ -772,7 +772,7 @@ def bar end context 'with a ternary' do - it 'registers an offense and corrects' do + it 'registers an offense but does not correct' do expect_offense(<<~RUBY) x = foo ? bar : bar ^^^ Move `bar` out of the conditional. diff --git a/spec/rubocop/cop/style/inverse_methods_spec.rb b/spec/rubocop/cop/style/inverse_methods_spec.rb index d9712adfc44e..77f5b16e3180 100644 --- a/spec/rubocop/cop/style/inverse_methods_spec.rb +++ b/spec/rubocop/cop/style/inverse_methods_spec.rb @@ -33,14 +33,9 @@ RUBY end - it 'registers an offense for safe navigation calling !.none? with a symbol proc' do - expect_offense(<<~RUBY) + it 'does not register an offense for safe navigation calling !.none? with a symbol proc' do + expect_no_offenses(<<~RUBY) !foo&.none?(&:even?) - ^^^^^^^^^^^^^^^^^^^^ Use `any?` instead of inverting `none?`. - RUBY - - expect_correction(<<~RUBY) - foo&.any?(&:even?) RUBY end @@ -55,14 +50,9 @@ RUBY end - it 'registers an offense for safe navigation calling !.none? with a block' do - expect_offense(<<~RUBY) + it 'does not register an offense for safe navigation calling !.none? with a block' do + expect_no_offenses(<<~RUBY) !foo&.none? { |f| f.even? } - ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `any?` instead of inverting `none?`. - RUBY - - expect_correction(<<~RUBY) - foo&.any? { |f| f.even? } RUBY end @@ -90,14 +80,9 @@ RUBY end - it 'registers an offense for safe navigation calling !.any? inside parens' do - expect_offense(<<~RUBY) + it 'does not register an offense for safe navigation calling !.any? inside parens' do + expect_no_offenses(<<~RUBY) !(foo&.any? &:working?) - ^^^^^^^^^^^^^^^^^^^^^^^ Use `none?` instead of inverting `any?`. - RUBY - - expect_correction(<<~RUBY) - foo&.none? &:working? RUBY end @@ -168,17 +153,17 @@ def test_method odd?: :even?, blank?: :present?, exclude?: :include? }.each do |method, inverse| - it "registers an offense for !foo.#{method}" do - expect_offense(<<~RUBY, method: method) - !foo.%{method} - ^^^^^^{method} Use `#{inverse}` instead of inverting `#{method}`. - RUBY + it "registers an offense for !foo.#{method}" do + expect_offense(<<~RUBY, method: method) + !foo.%{method} + ^^^^^^{method} Use `#{inverse}` instead of inverting `#{method}`. + RUBY - expect_correction(<<~RUBY) - foo.#{inverse} - RUBY - end + expect_correction(<<~RUBY) + foo.#{inverse} + RUBY end + end { :== => :!=, :!= => :==, @@ -209,6 +194,18 @@ def test_method end end + it 'allows using `any?` method with safe navigation operator' do + expect_no_offenses(<<~RUBY) + !nullable&.any?(&:odd) + RUBY + end + + it 'allows using `none?` method with safe navigation operator' do + expect_no_offenses(<<~RUBY) + !nullable&.none?(&:odd) + RUBY + end + it 'allows comparing for relational comparison operator (`<`) with safe navigation operator' do expect_no_offenses(<<~RUBY) !nullable&.<(0) diff --git a/spec/rubocop/cop/style/keyword_parameters_order_spec.rb b/spec/rubocop/cop/style/keyword_parameters_order_spec.rb index 02e749b9bff2..7c5a1233eb88 100644 --- a/spec/rubocop/cop/style/keyword_parameters_order_spec.rb +++ b/spec/rubocop/cop/style/keyword_parameters_order_spec.rb @@ -93,6 +93,18 @@ def m arg, required1:, required2:, optional1: 1, optional2: 2, **rest, &block RUBY end + it 'registers an offense but does not autocorrect when the argument range contains comments' do + expect_offense(<<~RUBY) + def foo(optional: 123, + ^^^^^^^^^^^^^ Place optional keyword parameters at the end of the parameters list. + # Some explanation + required:) + end + RUBY + + expect_no_corrections + end + it 'does not register an offense when there are no `kwoptarg`s before `kwarg`s' do expect_no_offenses(<<~RUBY) def m(arg, required:, optional: 1) diff --git a/spec/rubocop/cop/style/redundant_condition_spec.rb b/spec/rubocop/cop/style/redundant_condition_spec.rb index b5d3df85328f..7f75dcb3bb71 100644 --- a/spec/rubocop/cop/style/redundant_condition_spec.rb +++ b/spec/rubocop/cop/style/redundant_condition_spec.rb @@ -571,6 +571,68 @@ def do_something(foo, ...) end context 'when `true` as the true branch' do + it 'does not register an offense when true is used as the true branch and the condition is a local variable' do + expect_no_offenses(<<~RUBY) + variable = do_something + + if variable + true + else + a + end + RUBY + end + + it 'does not register an offense when true is used as the true branch and the condition is an instance variable' do + expect_no_offenses(<<~RUBY) + if @variable + true + else + a + end + RUBY + end + + it 'does not register an offense when true is used as the true branch and the condition is a class variable' do + expect_no_offenses(<<~RUBY) + if @@variable + true + else + a + end + RUBY + end + + it 'does not register an offense when true is used as the true branch and the condition is a global variable' do + expect_no_offenses(<<~RUBY) + if $variable + true + else + a + end + RUBY + end + + it 'does not register an offense when true is used as the true branch and the condition is a constant' do + expect_no_offenses(<<~RUBY) + if CONST + true + else + a + end + RUBY + end + + it 'does not register an offense when true is used as the true branch and the condition is not a predicate method' do + expect_no_offenses(<<~RUBY) + if a[:key] + true + else + a + end + RUBY + end + it 'registers an offense and autocorrects when true is used as the true branch' do expect_offense(<<~RUBY) if a.zero? @@ -586,6 +648,31 @@ def do_something(foo, ...) RUBY end + it 'registers an offense and autocorrects when true is used as the true branch and the condition uses safe navigation' do + expect_offense(<<~RUBY) + if a&.zero? + ^^^^^^^^^^^ Use double pipes `||` instead. + true + else + a + end + RUBY + + expect_correction(<<~RUBY) + a&.zero? || a + RUBY + end + + it 'does not register an offense when false is used as the else branch and the condition is not a predicate method' do + expect_no_offenses(<<~RUBY) + if !a[:key] + a + else + false + end + RUBY + end + it 'registers an offense and autocorrects when true is used as the true branch and the false branch is a string' do expect_offense(<<~RUBY) if b.nil? @@ -1042,4 +1129,18 @@ def do_something(foo, ...) end end end + + context 'when `AllowedMethods: nonzero?`' do + let(:cop_config) { { 'AllowedMethods' => ['nonzero?'] } } + + it 'does not register an offense when using `nonzero?`' do + expect_no_offenses(<<~RUBY) + if a.nonzero? + true + else + false + end + RUBY + end + end end diff --git a/spec/rubocop/cop/style/redundant_freeze_spec.rb b/spec/rubocop/cop/style/redundant_freeze_spec.rb index e4751e7ea701..d8ef639d7bff 100644 --- a/spec/rubocop/cop/style/redundant_freeze_spec.rb +++ b/spec/rubocop/cop/style/redundant_freeze_spec.rb @@ -26,6 +26,8 @@ it_behaves_like 'immutable objects', "('a' > 'b')" it_behaves_like 'immutable objects', '(a > b)' it_behaves_like 'immutable objects', '[1, 2, 3].size' + it_behaves_like 'immutable objects', '[1, 2, 3].count { |x| bar?(x) }' + it_behaves_like 'immutable objects', '[1, 2, 3].count { bar?(_1) }' shared_examples 'mutable objects' do |o| it "allows #{o} with freeze" do pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy