diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 243968b497f3..debf87d39600 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -38,7 +38,7 @@ output by `rubocop -V`, include them as well. Here's an example: ``` $ [bundle exec] rubocop -V -1.42.0 (using Parser 2.7.2.0, rubocop-ast 1.1.1, running on ruby 2.7.2) [x86_64-linux] +1.49.0 (using Parser 2.7.2.0, rubocop-ast 1.1.1, running on ruby 2.7.2) [x86_64-linux] - rubocop-performance 1.9.1 - rubocop-rspec 2.0.0 ``` diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index b8ee5b9c6b9d..cccbb432bf3e 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -1,10 +1,13 @@ name: Linting on: - - pull_request - - workflow_dispatch -permissions: # added using https://github.com/step-security/secure-workflows - contents: read - + pull_request: + paths: + - '**/*.yaml' + - '**/*.yml' + workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true jobs: yamllint: permissions: @@ -15,8 +18,10 @@ jobs: steps: - uses: actions/checkout@v3 - name: Yamllint - uses: karancode/yamllint-github-action@master + uses: karancode/yamllint-github-action@v2.1.1 with: + yamllint_strict: true + yamllint_format: parsable yamllint_comment: true env: GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/rubocop.yml b/.github/workflows/rubocop.yml index 2f9a7cac8a09..11cd336f2e61 100644 --- a/.github/workflows/rubocop.yml +++ b/.github/workflows/rubocop.yml @@ -12,6 +12,10 @@ on: permissions: # added using https://github.com/step-security/secure-workflows contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + jobs: main: name: >- @@ -55,7 +59,7 @@ jobs: run: bundle exec rake internal_investigation jruby: - name: JRuby 9.3 + name: JRuby 9.4 runs-on: ubuntu-latest steps: - name: checkout @@ -63,7 +67,7 @@ jobs: - name: set up Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: jruby-9.3 + ruby-version: jruby-9.4 bundler-cache: true - name: spec run: bundle exec rake spec diff --git a/.github/workflows/spell_checking.yml b/.github/workflows/spell_checking.yml index 030a7a20561e..eefea83cc227 100644 --- a/.github/workflows/spell_checking.yml +++ b/.github/workflows/spell_checking.yml @@ -5,26 +5,21 @@ on: [pull_request] permissions: # added using https://github.com/step-security/secure-workflows contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + jobs: codespell: name: Check spelling of all files with codespell runs-on: ubuntu-latest - strategy: - matrix: - python-version: [3.8] steps: - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + - uses: codespell-project/actions-codespell@v1 with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install codespell - if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - - name: Check spelling with codespell - run: codespell --ignore-words=codespell.txt || exit 1 + check_filenames: true + check_hidden: true + ignore_words_file: codespell.txt misspell: name: Check spelling of all files in commit with misspell runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index ccd1453c353d..5523a1c6c351 100644 --- a/.gitignore +++ b/.gitignore @@ -69,7 +69,6 @@ TAGS # For bundle binstubs bin/* -!bin/rubocop-profile # For stackprof or others tmp/* diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index d879c9388621..cfd922ff167b 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -1,5 +1,6 @@ - id: rubocop name: rubocop + description: Enforce the community Ruby Style Guide with RuboCop entry: rubocop language: ruby types: ['ruby'] diff --git a/.rubocop.yml b/.rubocop.yml index 779ff416e7f5..434c4ad12059 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -80,6 +80,11 @@ Lint/UselessAccessModifier: - 'def_matcher' - 'def_node_matcher' +Lint/EmptyFile: + Exclude: + # This file is intentionally empty to catch rubocop cops failing on empty files. + - spec/rubocop/intentionally_empty_file.rb + Metrics/BlockLength: Exclude: - 'Rakefile' @@ -100,6 +105,9 @@ Naming/InclusiveLanguage: Enabled: true CheckStrings: true FlaggedTerms: + ' a offense': + Suggestions: + - an offense auto-correct: Suggestions: - autocorrect diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index ca019b555c43..71b925626db4 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -23,7 +23,7 @@ Metrics/MethodLength: # Offense count: 8 # Configuration parameters: CountComments, CountAsOne. Metrics/ModuleLength: - Max: 136 + Max: 138 # Offense count: 9 RSpec/AnyInstance: diff --git a/CHANGELOG.md b/CHANGELOG.md index 38ae339dd60a..fd9a4dc35be9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,270 @@ # Change log + + ## master (unreleased) +## 1.49.0 (2023-04-03) + +### New features + +* [#11696](https://github.com/rubocop/rubocop/issues/11696): Add new `Style/DataInheritance` cop. ([@ktopolski][]) +* [#11730](https://github.com/rubocop/rubocop/issues/11730): Fix an error for `Layout/HashAlignment` when using anonymous keyword rest arguments. ([@koic][]) +* [#11746](https://github.com/rubocop/rubocop/pull/11746): Make `Layout/EndAlignment` aware of pattern matching. ([@koic][]) +* [#11750](https://github.com/rubocop/rubocop/pull/11750): Make `Metrics/BlockNesting` aware of numbered parameter. ([@koic][]) +* [#11699](https://github.com/rubocop/rubocop/issues/11699): Make `Style/ClassEqualityComparison` aware of `Class#to_s` and `Class#inspect` for class equality comparison. ([@koic][]) +* [#11737](https://github.com/rubocop/rubocop/pull/11737): Make `Style/MapToHash` and `Style/MapToSet` aware of numbered parameters. ([@koic][]) +* [#11732](https://github.com/rubocop/rubocop/issues/11732): Make `Style/MapToHash` and `Style/MapToSet` aware of symbol proc. ([@koic][]) +* [#11703](https://github.com/rubocop/rubocop/pull/11703): Make `Naming/InclusiveLanguage` support autocorrection when there is only one suggestion. ([@koic][]) + +### Bug fixes + +* [#11704](https://github.com/rubocop/rubocop/issues/11704): Fix a false positive for `Lint/UselessMethodDefinition` when method definition with non access modifier containing only `super` call. ([@koic][]) +* [#11723](https://github.com/rubocop/rubocop/issues/11723): Fix a false positive for `Style/IfUnlessModifier` when using one-line pattern matching as a `if` condition. ([@koic][]) +* [#11725](https://github.com/rubocop/rubocop/issues/11725): Fix an error when insufficient permissions to server cache dir are granted. ([@koic][]) +* [#11715](https://github.com/rubocop/rubocop/issues/11715): Ensure default configuration loads. ([@koic][]) +* [#11742](https://github.com/rubocop/rubocop/pull/11742): Fix error handling in bundler standalone mode. ([@composerinteralia][]) +* [#11712](https://github.com/rubocop/rubocop/pull/11712): Fix a crash in `Lint/EmptyConditionalBody`. ([@gsamokovarov][]) +* [#11641](https://github.com/rubocop/rubocop/issues/11641): Fix a false negative for `Layout/ExtraSpacing` when there are many comments with extra spaces. ([@nobuyo][]) +* [#11740](https://github.com/rubocop/rubocop/pull/11740): Fix a false positive for `Lint/NestedMethodDefinition` when nested definition inside `*_eval` and `*_exec` method call with a numblock. ([@ydah][]) +* [#11685](https://github.com/rubocop/rubocop/issues/11685): Fix incorrect directive comment insertion when percent array violates `Layout/LineLength` cop. ([@nobuyo][]) +* [#11706](https://github.com/rubocop/rubocop/issues/11706): Fix infinite loop when `--disable-uncorrectable` option and there is a multi-line percent array violates `Layout/LineLength`. ([@nobuyo][]) +* [#11697](https://github.com/rubocop/rubocop/issues/11697): Fix `Lint/Syntax` behavior when `--only` is not given the cop name. ([@koic][]) +* [#11709](https://github.com/rubocop/rubocop/pull/11709): Fix value omission false positive in `Style/MethodCallWithArgsParentheses`. ([@gsamokovarov][]) + +### Changes + +* [#11739](https://github.com/rubocop/rubocop/pull/11739): Make `Style/RedundantParentheses` aware of redundant method argument parentheses. ([@koic][]) +* [#10766](https://github.com/rubocop/rubocop/issues/10766): Use the path given by `--cache-root` to be the parent for `rubocop_cache` dir like other ways to specify it. ([@nobuyo][]) + +## 1.48.1 (2023-03-13) + +### Bug fixes + +* [#11673](https://github.com/rubocop/rubocop/pull/11673): Fix incorrect `Style/HashSyntax` autocorrection for assignment methods. ([@gsamokovarov][]) +* [#11682](https://github.com/rubocop/rubocop/issues/11682): Fix a false positive for `Lint/UselessRescue` when using `Thread#raise` in `rescue` clause. ([@koic][]) +* [#11672](https://github.com/rubocop/rubocop/issues/11672): Fix an error for `Layout/BlockEndNewline` when multiline block `}` is not on its own line and it is used as multiple arguments. ([@koic][]) +* [#11675](https://github.com/rubocop/rubocop/pull/11675): `Style/AccessorGrouping`: Fix sibling detection for methods with type sigs. ([@issyl0][]) +* [#11658](https://github.com/rubocop/rubocop/issues/11658): Fix `Lint/Debugger` should not allow pry. ([@ThHareau][]) +* [#11689](https://github.com/rubocop/rubocop/pull/11689): Fix `Lint/Syntax` behavior when `Enabled: false` of `Lint` department. ([@koic][]) +* [#11677](https://github.com/rubocop/rubocop/issues/11677): Fix the severity for `Lint/Syntax`. ([@koic][]) +* [#11691](https://github.com/rubocop/rubocop/pull/11691): Fix an error for `Gemspec/DependencyVersion` when method called on gem name argument for `add_dependency`. ([@koic][]) + +## 1.48.0 (2023-03-06) + +### New features + +* [#11628](https://github.com/rubocop/rubocop/issues/11628): Add new `Style/DirEmpty` cop. ([@ydah][]) +* [#11629](https://github.com/rubocop/rubocop/issues/11629): Add new `Style/FileEmpty` cop. ([@ydah][]) + +### Bug fixes + +* [#11654](https://github.com/rubocop/rubocop/pull/11654): Fix a false positive for `Lint/MissingSuper` when no `super` call and when defining some method. ([@koic][]) +* [#11661](https://github.com/rubocop/rubocop/pull/11661): Fix an error for `Style/Documentation` when namespace is a variable. ([@koic][]) +* [#11647](https://github.com/rubocop/rubocop/pull/11647): Fix an error for `Style/IfWithBooleanLiteralBranches` when using `()` as a condition. ([@koic][]) +* [#11646](https://github.com/rubocop/rubocop/pull/11646): Fix an error for `Style/NegatedIfElseCondition` when using `()` as a condition. ([@koic][]) +* [#11659](https://github.com/rubocop/rubocop/pull/11659): Fix an incorrect autocorrect for `Lint/OrAssignmentToConstant` when using or-assignment to a constant in method definition. ([@koic][]) +* [#11663](https://github.com/rubocop/rubocop/issues/11663): Fix an incorrect autocorrect for `Style/BlockDelimiters` when multi-line blocks to `{` and `}` with arithmetic operation method chain. ([@koic][]) +* [#11638](https://github.com/rubocop/rubocop/pull/11638): Fix a false positive for `Lint/UselessAccessModifier` when using same access modifier inside and outside the `included` block. ([@ydah][]) +* [#11164](https://github.com/rubocop/rubocop/issues/11164): Suppress server mode message with `-f json`. ([@jasondoc3][]) +* [#11643](https://github.com/rubocop/rubocop/pull/11643): Fix incorrect shorthand autocorrections in calls inside parentheses. ([@gsamokovarov][]) +* [#11650](https://github.com/rubocop/rubocop/pull/11650): `Style/AccessorGrouping`: Fix detection of Sorbet `sig {}` blocks. ([@issyl0][]) +* [#11657](https://github.com/rubocop/rubocop/issues/11657): Use cop name to check if cop inside registry is enabled. Previously, it was able to cause large memory usage during linting. ([@fatkodima][]) + +### Changes + +* [#11482](https://github.com/rubocop/rubocop/issues/11482): Avoid comment deletion by `Style/IfUnlessModifier` when the modifier form expression has long comment. ([@nobuyo][]) +* [#11649](https://github.com/rubocop/rubocop/issues/11649): Support `MinBranchesCount` config for `Style/CaseLikeIf` cop. ([@fatkodima][]) + +## 1.47.0 (2023-03-01) + +### New features + +* [#11475](https://github.com/rubocop/rubocop/pull/11475): Add autocorrect for hash in `Lint/LiteralInInterpolation`. ([@KessaPassa][]) +* [#11584](https://github.com/rubocop/rubocop/pull/11584): Add `Metrics/CollectionLiteralLength` cop. ([@sambostock][]) + +### Bug fixes + +* [#11615](https://github.com/rubocop/rubocop/issues/11615): Fix a false negative for `Lint/MissingSuper` when no `super` call with `Class.new` block. ([@koic][]) +* [#11615](https://github.com/rubocop/rubocop/issues/11615): Fix a false negative for `Lint/MissingSuper` when using `Class.new` without parent class argument. ([@koic][]) +* [#11040](https://github.com/rubocop/rubocop/issues/11040): Fix a false positive for `Style/IfUnlessModifier` when `defined?`'s argument value is undefined. ([@koic][]) +* [#11607](https://github.com/rubocop/rubocop/issues/11607): Fix a false positive for `Style/RedundantRegexpEscape` when an escaped hyphen follows after an escaped opening square bracket within a character class. ([@SparLaimor][]) +* [#11626](https://github.com/rubocop/rubocop/issues/11626): Fix a false positive for `Style/ZeroLengthPredicate` when using `File.new(path).size.zero?`. ([@koic][]) +* [#11620](https://github.com/rubocop/rubocop/pull/11620): Fix an error for `Lint/ConstantResolution` when using `__ENCODING__`. ([@koic][]) +* [#11625](https://github.com/rubocop/rubocop/pull/11625): Fix an error for `Lint/EmptyConditionalBody` when missing `if` body and using method call for return value. ([@koic][]) +* [#11631](https://github.com/rubocop/rubocop/issues/11631): Fix an incorrect autocorrect for `Style/ArgumentsForwarding` when using arguments forwarding for `.()` call. ([@koic][]) +* [#11621](https://github.com/rubocop/rubocop/issues/11621): Fix an incorrect autocorrect for `Layout/ClassStructure` using heredoc inside method. ([@fatkodima][]) +* [#3591](https://github.com/rubocop/rubocop/issues/3591): Handle modifier `while` and `until` expressions in `Lint/UselessAssignment`. ([@bfad][]) +* [#11202](https://github.com/rubocop/rubocop/issues/11202): Fixed usage of `--only` flag with `--auto-gen-config`. ([@istvanfazakas][]) + +### Changes + +* [#11623](https://github.com/rubocop/rubocop/pull/11623): Add rubocop-capybara to suggested extensions and extension doc. ([@ydah][]) + +## 1.46.0 (2023-02-22) + +### New features + +* [#11569](https://github.com/rubocop/rubocop/pull/11569): Support `TargetRubyVersion 3.3` (experimental). ([@koic][]) + +### Bug fixes + +* [#11574](https://github.com/rubocop/rubocop/pull/11574): Fix a broken shorthand syntax autocorrection. ([@gsamokovarov][]) +* [#11599](https://github.com/rubocop/rubocop/pull/11599): Fix a false positive for `Layout/LineContinuationSpacing` when using percent literals. ([@koic][]) +* [#11556](https://github.com/rubocop/rubocop/issues/11556): Fix a false positive for `Lint/Debugger` when `p` is an argument of method call. ([@koic][]) +* [#11591](https://github.com/rubocop/rubocop/issues/11591): Fix a false positive for `Lint/ToEnumArguments` when enumerator is not created for `__callee__` and `__callee__` methods. ([@koic][]) +* [#11603](https://github.com/rubocop/rubocop/pull/11603): Actually run temporarily enabled cops. ([@tdeo][]) +* [#11579](https://github.com/rubocop/rubocop/pull/11579): Fix an error for `Layout/HeredocArgumentClosingParenthesis` when heredoc is a method argument in a parenthesized block argument. ([@koic][]) +* [#11576](https://github.com/rubocop/rubocop/pull/11576): Fix an error for `Lint/UselessRescue` when `rescue` does not exception variable and `ensure` has empty body. ([@koic][]) +* [#11608](https://github.com/rubocop/rubocop/pull/11608): Fix an error for `Lint/RefinementImportMethods` when using `include` on the top level. ([@koic][]) +* [#11589](https://github.com/rubocop/rubocop/pull/11589): Fix an error for `Layout/HeredocArgumentClosingParenthesis` when heredoc is a branch body in a method argument of a parenthesized argument. ([@koic][]) +* [#11567](https://github.com/rubocop/rubocop/issues/11567): Fix `Layout/EndAlignment` false negative. ([@j-miyake][]) +* [#11582](https://github.com/rubocop/rubocop/issues/11582): Fix checking if token with large offset begins its line. ([@fatkodima][]) +* [#11412](https://github.com/rubocop/rubocop/issues/11412): Mark `Style/ArrayIntersect` as unsafe. ([@koic][]) +* [#11559](https://github.com/rubocop/rubocop/pull/11559): Fixed false positives and negatives in `Style/RedundantRegexpCharacterClass` when using octal escapes (e.g. "\0"). ([@jaynetics][]) +* [#11575](https://github.com/rubocop/rubocop/pull/11575): Fix parentheses in value omissions for multiple assignments. ([@gsamokovarov][]) + +### Changes + +* [#11586](https://github.com/rubocop/rubocop/issues/11586): Handle `ruby2_keywords` in `Style/DocumentationMethod` cop. ([@fatkodima][]) +* [#11604](https://github.com/rubocop/rubocop/issues/11604): Make `Naming/VariableNumber` to allow `x86_64` CPU architecture name by default. ([@koic][]) +* [#11596](https://github.com/rubocop/rubocop/issues/11596): Make `Style/AccessorGrouping` aware of method call before accessor. ([@koic][]) +* [#11588](https://github.com/rubocop/rubocop/pull/11588): Optimize `Style/WordArray` complex matrix check. ([@sambostock][]) +* [#11573](https://github.com/rubocop/rubocop/pull/11573): Handle hash patterns and pins in `Lint/OutOfRangeRegexpRef` cop. ([@fatkodima][]) +* [#11564](https://github.com/rubocop/rubocop/pull/11564): Remove print debug methods from default for `Lint/Debugger`. ([@koic][]) + +## 1.45.1 (2023-02-08) + +### Bug fixes + +* [#11552](https://github.com/rubocop/rubocop/pull/11552): Fix a false positive for `Lint/Debugger` when methods containing different method chains. ([@ydah][]) +* [#11548](https://github.com/rubocop/rubocop/pull/11548): Fix an error for `Style/AccessModifierDeclarations` when if a non method definition was included. ([@ydah][]) +* [#11554](https://github.com/rubocop/rubocop/issues/11554): Fix an error for `Style/RedundantCondition` when the branches contains empty hash literal argument. ([@koic][]) +* [#11549](https://github.com/rubocop/rubocop/issues/11549): Fix an error for third party cops when inheriting `RuboCop::Cop::Cop`. ([@koic][]) + +## 1.45.0 (2023-02-08) + +### New features + +* [#10839](https://github.com/rubocop/rubocop/pull/10839): Add API for 3rd party template support. ([@r7kamura][]) +* [#11528](https://github.com/rubocop/rubocop/pull/11528): Add new `Style/RedundantHeredocDelimiterQuotes` cop. ([@koic][]) +* [#11188](https://github.com/rubocop/rubocop/issues/11188): Add a `--no-detach` option for `--start-server`. This will start the server process in the foreground, which can be helpful when running within Docker where detaching the process terminates the container. ([@f1sherman][]) +* [#11546](https://github.com/rubocop/rubocop/pull/11546): Make `Lint/UselessAccessModifier` aware of Ruby 3.2's `Data.define`. ([@koic][]) +* [#11396](https://github.com/rubocop/rubocop/pull/11396): Add ability to profile rubocop execution via `--profile` and `--memory` options. ([@fatkodima][]) + +### Bug fixes + +* [#11491](https://github.com/rubocop/rubocop/pull/11491): Fix a crash on `Lint/UselessAssignment`. ([@gsamokovarov][]) +* [#11515](https://github.com/rubocop/rubocop/pull/11515): Fix a false negative for `Naming/HeredocDelimiterNaming` when using lowercase. ([@koic][]) +* [#11511](https://github.com/rubocop/rubocop/issues/11511): Fix a false negative for `Style/YodaCondition` when using constant. ([@koic][]) +* [#11520](https://github.com/rubocop/rubocop/pull/11520): Fix a false negative for `Style/YodaExpression` when using constant. ([@koic][]) +* [#11521](https://github.com/rubocop/rubocop/issues/11521): Fix a false positive for `Lint/FormatParameterMismatch` when using `Kernel.format` with the interpolated number of decimal places fields match. ([@koic][]) +* [#11545](https://github.com/rubocop/rubocop/pull/11545): Fix the following false positive for `Lint/NestedMethodDefinition` when using numbered parameter. ([@koic][]) +* [#11535](https://github.com/rubocop/rubocop/issues/11535): Fix a false positive for `Style/NumberedParametersLimit` when only `_2` or higher numbered parameter is used. ([@koic][]) +* [#11508](https://github.com/rubocop/rubocop/issues/11508): Fix a false positive for `Style/OperatorMethodCall` when using multiple arguments for operator method. ([@koic][]) +* [#11503](https://github.com/rubocop/rubocop/issues/11503): Fix a false positive for `Style/RedundantCondition` when using method argument with operator. ([@koic][]) +* [#11529](https://github.com/rubocop/rubocop/pull/11529): Fix an incorrect autocorrect for `Layout/ClassStructure` when definitions that need to be sorted are defined alternately. ([@ydah][]) +* [#11530](https://github.com/rubocop/rubocop/pull/11530): Fix an incorrect autocorrect for `Style/AccessModifierDeclarations` when multiple groupable access modifiers are defined. ([@ydah][]) +* [#10910](https://github.com/rubocop/rubocop/pull/10910): Fix an incorrect autocorrect for `Style/MultilineTernaryOperator` when contains a comment. ([@ydah][]) +* [#11522](https://github.com/rubocop/rubocop/pull/11522): Don't flag default keyword arguments in `Style/ArgumentsForwarding`. ([@splattael][]) +* [#11547](https://github.com/rubocop/rubocop/pull/11547): Fix a false positive for `Lint/NestedMethodDefinition` when using Ruby 3.2's `Data.define`. ([@koic][]) +* [#11537](https://github.com/rubocop/rubocop/pull/11537): Fix an infinite loop error for `Layout/ArrayAlignment` when using assigning unbracketed array elements. ([@koic][]) +* [#11516](https://github.com/rubocop/rubocop/pull/11516): Fix missing parentheses in shorthand hash syntax as argument calls. ([@gsamokovarov][]) + +### Changes + +* [#11504](https://github.com/rubocop/rubocop/issues/11504): Allow `initialize` method in `Style/DocumentationMethod`. ([@koic][]) +* [#11541](https://github.com/rubocop/rubocop/pull/11541): Enable autocorrection for `Layout/LineContinuationLeadingSpace`. ([@eugeneius][]) +* [#11542](https://github.com/rubocop/rubocop/pull/11542): Mark `Layout/AssignmentIndentation` as safe and `Lint/AssignmentInCondition` as unsafe for autocorrection. ([@eugeneius][]) +* [#11517](https://github.com/rubocop/rubocop/issues/11517): Make `Lint/Debugger` aware of `p`, `PP.pp`, and `pp` methods. ([@koic][]) +* [#11539](https://github.com/rubocop/rubocop/pull/11539): Remove `bundler` from default `AllowedGems` of `Gemspec/DevelopmentDependencies`. ([@koic][]) + +## 1.44.1 (2023-01-25) + +### Bug fixes + +* [#11492](https://github.com/rubocop/rubocop/issues/11492): Fix an error for `Lint/Void` when configuring `CheckForMethodsWithNoSideEffects: true`. ([@koic][]) +* [#11400](https://github.com/rubocop/rubocop/issues/11400): Fix an incorrect autocorrect for `Naming/BlockForwarding` and `Lint/AmbiguousOperator` when autocorrection conflicts for ambiguous splat argument. ([@fatkodima][]) +* [#11483](https://github.com/rubocop/rubocop/issues/11483): Fix `Layout/ClosingParenthesisIndentation` for keyword splat arguments. ([@fatkodima][]) +* [#11487](https://github.com/rubocop/rubocop/pull/11487): Fix a false positive for `Lint/FormatParameterMismatch` when format string is only interpolated string. ([@ydah][]) +* [#11485](https://github.com/rubocop/rubocop/issues/11485): Fix a false positive for `Lint/UselessAssignment` when using numbered block parameter. ([@koic][]) + +## 1.44.0 (2023-01-23) + +### New features + +* [#11410](https://github.com/rubocop/rubocop/issues/11410): Add new `Style/InvertibleUnlessCondition` cop. ([@fatkodima][]) +* [#11338](https://github.com/rubocop/rubocop/issues/11338): Add new `Style/ComparableClamp` cop. ([@koic][]) +* [#11350](https://github.com/rubocop/rubocop/issues/11350): Make `Lint/DeprecatedClassMethods` aware of deprecated `attr` with boolean 2nd argument. ([@koic][]) +* [#11457](https://github.com/rubocop/rubocop/pull/11457): Make `Metrics/BlockNesting` aware of pattern matching. ([@koic][]) +* [#11458](https://github.com/rubocop/rubocop/pull/11458): Make `Metrics/CyclomaticComplexity` aware of pattern matching. ([@koic][]) +* [#11469](https://github.com/rubocop/rubocop/pull/11469): Add `Gemspec/DevelopmentDependencies` cop. ([@sambostock][]) + +### Bug fixes + +* [#11445](https://github.com/rubocop/rubocop/issues/11445): Fix an incorrect autocorrect for `Style/BlockDelimiters` when there is a comment after the closing brace and bracket. ([@koic][]) +* [#11428](https://github.com/rubocop/rubocop/pull/11428): Apply value omission exceptions in super invocations. ([@gsamokovarov][]) +* [#11420](https://github.com/rubocop/rubocop/issues/11420): Fix a false positive for `Lint/UselessRescue` when using exception variable in `ensure` clause. ([@koic][]) +* [#11460](https://github.com/rubocop/rubocop/issues/11460): Fix an error for `Style/OperatorMethodCall` when using `foo.> 42`. ([@koic][]) +* [#11456](https://github.com/rubocop/rubocop/pull/11456): Fix value omissions in `yield` invocations. ([@gsamokovarov][]) +* [#11467](https://github.com/rubocop/rubocop/issues/11467): Fix a false negative for `Style/MethodCallWithoutArgsParentheses` when calling method on a receiver and assigning to a variable with the same name. ([@koic][]) +* [#11430](https://github.com/rubocop/rubocop/issues/11430): Fix an infinite loop error for `Layout/BlockEndNewline` when multiline blocks with newlines before the `; end`. ([@koic][]) +* [#11442](https://github.com/rubocop/rubocop/pull/11442): Fix a crash during anonymous rest argument forwarding. ([@gsamokovarov][]) +* [#11447](https://github.com/rubocop/rubocop/pull/11447): Fix an incorrect autocorrect for `Style/RedundantDoubleSplatHashBraces` when using nested double splat hash braces. ([@koic][]) +* [#11459](https://github.com/rubocop/rubocop/pull/11459): Make `Lint/UselessRuby2Keywords` aware of conditions. ([@splattael][]) +* [#11415](https://github.com/rubocop/rubocop/issues/11415): Fix a false positive for `Lint/UselessMethodDefinition` when method definition contains rest arguments. ([@koic][]) +* [#11418](https://github.com/rubocop/rubocop/issues/11418): Fix a false positive for `Style/MethodCallWithArgsParentheses` when using anonymous rest arguments or anonymous keyword rest arguments. ([@koic][]) +* [#11431](https://github.com/rubocop/rubocop/pull/11431): Fix a crash in `Style/HashSyntax`. ([@gsamokovarov][]) +* [#11444](https://github.com/rubocop/rubocop/issues/11444): Fix a false positive for `Lint/ShadowingOuterLocalVariable` when using numbered block parameter. ([@koic][]) +* [#11477](https://github.com/rubocop/rubocop/issues/11477): Fix an error when using YAML alias with server mode. ([@koic][]) +* [#11419](https://github.com/rubocop/rubocop/issues/11419): Fix a false positive for `Lint/RedundantRequireStatement` when using `pretty_inspect`. ([@koic][]) +* [#11439](https://github.com/rubocop/rubocop/issues/11439): Fix an incorrect autocorrect for `Style/MinMaxComparison` when using `a < b a : b` with `elsif/else`. ([@ydah][]) +* [#11464](https://github.com/rubocop/rubocop/pull/11464): Fix a false negative for `Lint/FormatParameterMismatch` when include interpolated string. ([@ydah][]) +* [#11425](https://github.com/rubocop/rubocop/pull/11425): Fix a false negative for `Lint/Void` when using methods that takes blocks. ([@krishanbhasin-shopify][]) +* [#11437](https://github.com/rubocop/rubocop/pull/11437): Fix an error for `Style/AccessModifierDeclarations` when access modifier is inlined with a method on the top level. ([@koic][]) +* [#11455](https://github.com/rubocop/rubocop/pull/11455): Fix crash with `super value_omission:` followed by a method call. ([@gsamokovarov][]) + +### Changes + +* [#11465](https://github.com/rubocop/rubocop/pull/11465): Make `Style/Semicolon` aware of redundant semicolon in block. ([@koic][]) +* [#11471](https://github.com/rubocop/rubocop/pull/11471): Change to not output not configured warning when renamed and pending cop. ([@ydah][]) + +## 1.43.0 (2023-01-10) + +### New features + +* [#11359](https://github.com/rubocop/rubocop/issues/11359): Add new `Lint/UselessRescue` cop. ([@fatkodima][]) +* [#11389](https://github.com/rubocop/rubocop/pull/11389): Add autocorrect for `Style/MissingElse`. ([@FnControlOption][]) + +### Bug fixes + +* [#11386](https://github.com/rubocop/rubocop/pull/11386): Fix a false positive for `Style/OperatorMethodCall` when using anonymous forwarding. ([@koic][]) +* [#11409](https://github.com/rubocop/rubocop/issues/11409): Fix an incorrect autocorrect for `Style/HashSyntax` when using hash value omission and `EnforcedStyle: no_mixed_keys`. ([@koic][]) +* [#11405](https://github.com/rubocop/rubocop/issues/11405): Fix undefined method `range_between' for `Style/WhileUntilModifier`. ([@such][]) +* [#11374](https://github.com/rubocop/rubocop/pull/11374): Fix an error for `Style/StringHashKeys` when using invalid symbol in encoding UTF-8 as keys. ([@koic][]) +* [#11392](https://github.com/rubocop/rubocop/pull/11392): Fix an incorrect autocorrect for `Style/RedundantDoubleSplatHashBraces` using double splat in double splat hash braces. ([@koic][]) +* [#8990](https://github.com/rubocop/rubocop/issues/8990): Make `Style/HashEachMethods` aware of built-in `Thread.current`. ([@koic][]) +* [#11390](https://github.com/rubocop/rubocop/issues/11390): Fix an incorrect autocorrect for `Style/HashSyntax` when hash first argument key and hash value only are the same which has a method call on the next line. ([@koic][]) +* [#11379](https://github.com/rubocop/rubocop/pull/11379): Fix a false negative for `Style/OperatorMethodCall` when using `a.+ b.something`. ([@koic][]) +* [#11180](https://github.com/rubocop/rubocop/issues/11180): Fix an error for `Style/RedundantRegexpEscape` when using `%r` to provide regexp expressions. ([@si-lens][]) +* [#11403](https://github.com/rubocop/rubocop/pull/11403): Fix bad offense for parenthesised calls in literals for `omit_parentheses` style in `Style/MethodCallWithArgsParentheses`. ([@gsamokovarov][]) +* [#11407](https://github.com/rubocop/rubocop/pull/11407): Fix an error for `Style/HashSyntax` when expression follows hash key assignment. ([@fatkodima][]) +* [#11377](https://github.com/rubocop/rubocop/issues/11377): Fix `Style/OperatorMethodCall` when forwarding arguments. ([@sambostock][]) + +### Changes + +* [#11382](https://github.com/rubocop/rubocop/pull/11382): Require `unicode-display_width` 2.4.0 or higher. ([@fatkodima][]) +* [#11381](https://github.com/rubocop/rubocop/pull/11381): Require Parser 3.2.0.0 or higher. ([@koic][]) +* [#11380](https://github.com/rubocop/rubocop/pull/11380): Disable `Style/YodaExpression` by default. ([@koic][]) +* [#11303](https://github.com/rubocop/rubocop/issues/11303): Make `Metrics/ParameterLists` aware of `Struct.new` and `Data.define` blocks. ([@koic][]) + ## 1.42.0 (2023-01-01) ### New features @@ -18,7 +281,7 @@ * [#11351](https://github.com/rubocop/rubocop/pull/11351): Fix an incorrect autocorrect for `Lint/RegexpAsCondition` when using regexp literal with bang. ([@koic][]) * [#11329](https://github.com/rubocop/rubocop/pull/11329): Accept simple freezed constants in `Layout/ClassStructure` and correctly handle class methods. ([@fatkodima][]) * [#11344](https://github.com/rubocop/rubocop/pull/11344): Fix an error for `Style/GuardClause` when using heredoc as an argument of raise in `then` branch and it does not have `else` branch. ([@koic][]) -* [#11335](https://github.com/rubocop/rubocop/pull/11335): Fix an error for `Style/RequireOrder` when only one `reuqire`. ([@koic][]) +* [#11335](https://github.com/rubocop/rubocop/pull/11335): Fix an error for `Style/RequireOrder` when only one `require`. ([@koic][]) * [#11348](https://github.com/rubocop/rubocop/pull/11348): Fix an error for `Style/SelectByRegexp` when block body is empty. ([@koic][]) * [#11320](https://github.com/rubocop/rubocop/issues/11320): Fix a false positive for `Lint/RequireParentheses` when assigning ternary operator. ([@koic][]) * [#11361](https://github.com/rubocop/rubocop/issues/11361): Make `Style/MethodDefParentheses` aware of Ruby 3.2's anonymous rest and keyword rest arguments. ([@koic][]) @@ -63,7 +326,7 @@ * [#11255](https://github.com/rubocop/rubocop/pull/11255): Fix an error for `Style/RequireOrder` when `require` with no arguments is put between `require`. ([@ydah][]) * [#11273](https://github.com/rubocop/rubocop/issues/11273): Fix a false positive for `Lint/DuplicateMethods` when there are same `alias_method` name outside `rescue` or `ensure` scopes. ([@koic][]) -* [#11267](https://github.com/rubocop/rubocop/issues/11267): Fix an error for Style/RequireOrder when modifier conditional is used between `require`. ([@ydah][]) +* [#11267](https://github.com/rubocop/rubocop/issues/11267): Fix an error for `Style/RequireOrder` when modifier conditional is used between `require`. ([@ydah][]) * [#11254](https://github.com/rubocop/rubocop/pull/11254): Fix an error for `Style/RequireOrder` when `require` is a method argument. ([@koic][]) * [#11266](https://github.com/rubocop/rubocop/issues/11266): Fix a false positive for `Style/RedundantConstantBase` when enabling `Lint/ConstantResolution`. ([@koic][]) * [#11296](https://github.com/rubocop/rubocop/pull/11296): Fix an error for `Lint/NonAtomicFileOperation` when use file existence checks line break `unless` by postfix before creating file. ([@koic][]) @@ -101,11 +364,11 @@ * [#11239](https://github.com/rubocop/rubocop/issues/11239): Fix an incorrect autocorrect for `Style/GuardClause` when using heredoc as an argument of raise in branch body. ([@koic][]) * [#11182](https://github.com/rubocop/rubocop/issues/11182): Fix an incorrect autocorrect for `EnforcedShorthandSyntax: always` of `Style/HashSyntax` with `Style/IfUnlessModifier` when using Ruby 3.1. ([@koic][]) * [#11184](https://github.com/rubocop/rubocop/issues/11184): Fix an error for `Lint/ShadowingOuterLocalVariable` when a block local variable has same name as an outer `until` scope variable. ([@koic][]) -* [#11198](https://github.com/rubocop/rubocop/pull/11198): Fix an error for `Lint/EmptyConditionalBody` when one using line if/;/end without then boby. ([@koic][]) +* [#11198](https://github.com/rubocop/rubocop/pull/11198): Fix an error for `Lint/EmptyConditionalBody` when one using line if/;/end without then body. ([@koic][]) * [#11196](https://github.com/rubocop/rubocop/issues/11196): Fix a false positive for `Style/GuardClause` when using `raise` in `then` body of `if..elsif..end` form. ([@koic][]) * [#11213](https://github.com/rubocop/rubocop/pull/11213): Support redundant department disable in scope of `Lint/RedundantCopDisableDirective` cop. ([@isarcasm][]) * [#11200](https://github.com/rubocop/rubocop/issues/11200): Fix an incorrect autocorrect for `Layout/MultilineMethodCallBraceLayout` when using method chain for heredoc argument in multiline literal brace layout. ([@koic][]) -* [#11190](https://github.com/rubocop/rubocop/pull/11190): Fix an error for `Style/IfWithSemicolon` when using one line if/;/end without then boby. ([@koic][]) +* [#11190](https://github.com/rubocop/rubocop/pull/11190): Fix an error for `Style/IfWithSemicolon` when using one line if/;/end without then body. ([@koic][]) * [#11244](https://github.com/rubocop/rubocop/pull/11244): Fix a false negative for `Style/RedundantReturn` when dynamic define methods. ([@ydah][]) ### Changes @@ -205,7 +468,7 @@ ### Bug fixes * [#11034](https://github.com/rubocop/rubocop/issues/11034): Fix server mode behavior when using `--stderr`. ([@tdeo][]) -* [#11028](https://github.com/rubocop/rubocop/issues/11028): Fix a false positive for `Lint/RequireParentheses` when using ternary operator in square bracksts. ([@koic][]) +* [#11028](https://github.com/rubocop/rubocop/issues/11028): Fix a false positive for `Lint/RequireParentheses` when using ternary operator in square brackets. ([@koic][]) * [#11051](https://github.com/rubocop/rubocop/issues/11051): Preserve comments on `Style/AccessModifierDeclarations` autocorrection. ([@r7kamura][]) * [#9116](https://github.com/rubocop/rubocop/issues/9116): Support `super` method in `Layout/FirstArgumentIndentation`. ([@tdeo][]) * [#11068](https://github.com/rubocop/rubocop/pull/11068): Fix a false positive for `Style/RedundantRegexpCharacterClass` when using starting with "\0" number. ([@koic][]) @@ -220,7 +483,7 @@ * [#11022](https://github.com/rubocop/rubocop/issues/11022): Fix an incorrect autocorrect for `Style/RedundantCondition` when using redundant brackets access condition. ([@koic][]) * [#11037](https://github.com/rubocop/rubocop/issues/11037): Fix a false positive for `Style/CollectionCompact` when using `to_enum.reject` or `lazy.reject` methods with Ruby 3.0 or lower. ([@koic][]) * [#11017](https://github.com/rubocop/rubocop/pull/11017): Fix an autocorrect for `Lint/EmptyConditionalBody` that causes a SyntaxError when missing `if` and `else` body. ([@ydah][]) -* [#11047](https://github.com/rubocop/rubocop/issues/11047): Fix an incorrect autocorrect for `Style/SafeNavigationChain` when using `+@` and `-@` methods. ([@koic][]) +* [#11047](https://github.com/rubocop/rubocop/issues/11047): Fix an incorrect autocorrect for `Lint/SafeNavigationChain` when using `+@` and `-@` methods. ([@koic][]) * [#11015](https://github.com/rubocop/rubocop/pull/11015): Fix a false positive for `Style/HashSyntax` when without parentheses call expr follows after nested method call. ([@koic][]) * [#11067](https://github.com/rubocop/rubocop/issues/11067): Fix a false positive for `Lint/DuplicateRegexpCharacterClassElement` when using regexp character starts with escaped zero number. ([@koic][]) * [#11030](https://github.com/rubocop/rubocop/issues/11030): Fix an incorrect autocorrect for `Lint/UnusedMethodArgument` and `Style::ExplicitBlockArgument` when autocorrection conflicts for `&block` argument. ([@koic][]) @@ -289,7 +552,7 @@ * [#10895](https://github.com/rubocop/rubocop/issues/10895): Fix incorrect autocomplete in `Style/RedundantParentheses` when a heredoc is used in an array. ([@dvandersluis][]) * [#10909](https://github.com/rubocop/rubocop/pull/10909): Fix loading behavior on running without `bundle exec`. ([@r7kamura][]) * [#10913](https://github.com/rubocop/rubocop/issues/10913): Make `Style/ArgumentsForwarding` aware of anonymous block argument. ([@koic][]) -* [#10911](https://github.com/rubocop/rubocop/pull/10911): Fix Style/ClassMethodsDefinitions for non-self receivers. ([@sambostock][]) +* [#10911](https://github.com/rubocop/rubocop/pull/10911): Fix `Style/ClassMethodsDefinitions` for non-self receivers. ([@sambostock][]) ### Changes @@ -376,7 +639,7 @@ * Add EnforcedStyle (leading/trailing) configuration to `Layout::LineContinuationLeadingSpace`. ([@bquorning][]) * [#10784](https://github.com/rubocop/rubocop/pull/10784): Preserve multiline semantics on `Style/SymbolArray` and `Style/WordArray`. ([@r7kamura][]) * [#10814](https://github.com/rubocop/rubocop/pull/10814): Avoid buffering stdout when running in server mode. ([@ccutrer][]) -* [#10817](https://github.com/rubocop/rubocop/pull/10817): Add autocorrect support for `Style/SafeNavigationChain`. ([@r7kamura][]) +* [#10817](https://github.com/rubocop/rubocop/pull/10817): Add autocorrect support for `Lint/SafeNavigationChain`. ([@r7kamura][]) * [#10810](https://github.com/rubocop/rubocop/pull/10810): Support safe navigation operator on `Style/SymbolProc`. ([@r7kamura][]) * [#10803](https://github.com/rubocop/rubocop/pull/10803): Require RuboCop AST 1.9.1 or higher. ([@koic][]) @@ -385,7 +648,7 @@ ### Bug fixes * [#10774](https://github.com/rubocop/rubocop/pull/10774): Fix false negatives in `Style/DocumentationMethod` when a public method is defined after a private one. ([@Darhazer][]) -* [#10764](https://github.com/rubocop/rubocop/issues/10764): Fix performance issue for Layout/FirstHashElementIndentation and Layout/FirstArrayElementIndentation. ([@j-miyake][]) +* [#10764](https://github.com/rubocop/rubocop/issues/10764): Fix performance issue for `Layout/FirstHashElementIndentation` and `Layout/FirstArrayElementIndentation`. ([@j-miyake][]) * [#10780](https://github.com/rubocop/rubocop/issues/10780): Fix an error when using `rubocop:auto_correct` deprecated custom rake task. ([@koic][]) * [#10786](https://github.com/rubocop/rubocop/issues/10786): Fix a false positive for `Lint/NonAtomicFileOperation` when using complex conditional. ([@koic][]) * [#10785](https://github.com/rubocop/rubocop/pull/10785): Fix a false negative for `Style/RedundantParentheses` when parens around a receiver of a method call with an argument. ([@koic][]) @@ -400,7 +663,7 @@ * [#10760](https://github.com/rubocop/rubocop/issues/10760): Fix a false positive for `Lint/NonAtomicFileOperation` when using `FileTest.exist?` with `if` condition that has `else` branch. ([@koic][]) * [#10745](https://github.com/rubocop/rubocop/issues/10745): Require JSON 2.3 or higher to fix an incompatible JSON API error. ([@koic][]) * [#10754](https://github.com/rubocop/rubocop/issues/10754): Fix an incorrect autocorrect for `Style/HashExcept` when using a non-literal collection receiver for `include?`. ([@koic][]) -* [#10751](https://github.com/rubocop/rubocop/issues/10751): Fix autocorrect for Layout/FirstHashElementIndentation. ([@j-miyake][]) +* [#10751](https://github.com/rubocop/rubocop/issues/10751): Fix autocorrect for `Layout/FirstHashElementIndentation`. ([@j-miyake][]) * [#10750](https://github.com/rubocop/rubocop/pull/10750): Recover 7x slow running `rubocop`. ([@koic][]) ## 1.31.0 (2022-06-27) @@ -424,8 +687,8 @@ * Fix `rubocop -V` not displaying the version information for rubocop-graphql, rubocop-md and rubocop-thread_safety. ([@Darhazer][]) * [#10711](https://github.com/rubocop/rubocop/issues/10711): Fix an error for `Style/MultilineTernaryOperator` when the false branch is on a separate line. ([@koic][]) * [#10719](https://github.com/rubocop/rubocop/issues/10719): Fix a false positive for `Lint/ParenthesesAsGroupedExpression` when using safe navigation operator. ([@koic][]) -* [#10736](https://github.com/rubocop/rubocop/pull/10736): Fix Layout/SpaceInsideBlockBraces for blocks with numbered arguments. ([@gsamokovarov][]) -* [#10749](https://github.com/rubocop/rubocop/pull/10749): Fix Style/BlockDelimiters for blocks with numbered arguments. ([@gsamokovarov][]) +* [#10736](https://github.com/rubocop/rubocop/pull/10736): Fix `Layout/SpaceInsideBlockBraces` for blocks with numbered arguments. ([@gsamokovarov][]) +* [#10749](https://github.com/rubocop/rubocop/pull/10749): Fix `Style/BlockDelimiters` for blocks with numbered arguments. ([@gsamokovarov][]) * [#10737](https://github.com/rubocop/rubocop/issues/10737): Fix crash in `Style/ConditionalAssignment` with `EnforcedStyle: assign_inside_condition` when op-assigning a variable inside a `resbody`. ([@dvandersluis][]) * [#7900](https://github.com/rubocop/rubocop/issues/7900): Fix `Style/FormatStringToken` false positive with formatted input and `template` style enforced, and add autocorrection. ([@FnControlOption][]) @@ -434,7 +697,7 @@ * [#10730](https://github.com/rubocop/rubocop/pull/10730): Change output timing of GitHubActionsFormatter. ([@r7kamura][]) * [#10709](https://github.com/rubocop/rubocop/pull/10709): Deprecate `rubocop:auto_correct` custom rake task and newly split `rubocop:autocorrect` and `rubocop:autocorrect-all` custom rake tasks. ([@koic][]) * [#9760](https://github.com/rubocop/rubocop/issues/9760): Change RangeHelp#range_with_surrounding_space to allow passing the range as a positional argument. ([@pirj][]) -* [#10693](https://github.com/rubocop/rubocop/issues/10693): Add ignore case for `Style/EmptyLinesAroundAttributeAccessor` when there is a comment line on the next line. ([@ydah][]) +* [#10693](https://github.com/rubocop/rubocop/issues/10693): Add ignore case for `Layout/EmptyLinesAroundAttributeAccessor` when there is a comment line on the next line. ([@ydah][]) * [#10245](https://github.com/rubocop/rubocop/pull/10245): **(Breaking)** integrate `Gemspec/DateAssignment` into `Gemspec/DeprecatedAttributeAssignment`. ([@kaitielth][]) * [#10697](https://github.com/rubocop/rubocop/pull/10697): Restore `Lint/UselessElseWithoutRescue` cop. ([@koic][]) * [#10740](https://github.com/rubocop/rubocop/pull/10740): Make `Style/GuardClause` a bit more lenient when the replacement would make the code more verbose. ([@dvandersluis][]) @@ -551,7 +814,7 @@ * [#10551](https://github.com/rubocop/rubocop/pull/10551): Add `AllowComments` option to `Style/RedundantInitialize` is true by default. ([@koic][]) * [#10552](https://github.com/rubocop/rubocop/pull/10552): Support autocorrection for `Style/RedundantInitialize`. ([@koic][]) -* [#10441](https://github.com/rubocop/rubocop/pull/10441): Add Security/CompoundHash Cop. ([@sambostock][], [@chrisseaton][]) +* [#10441](https://github.com/rubocop/rubocop/pull/10441): Add `Security/CompoundHash` cop. ([@sambostock][], [@chrisseaton][]) * [#10521](https://github.com/rubocop/rubocop/pull/10521): Add `use_builtin_english_names` style to `Style/SpecialGlobalVars`. ([@splattael][]) * [#10522](https://github.com/rubocop/rubocop/issues/10522): Add new `Style/ObjectThen` cop. ([@ydah][]) * [#10502](https://github.com/rubocop/rubocop/pull/10502): Add new `Style/FetchEnvVar` cop. ([@johnny-miyake][]) @@ -630,8 +893,8 @@ * [#10406](https://github.com/rubocop/rubocop/pull/10406): Fix a false positive for `Lint/InheritException` when inheriting a standard lib exception class that is not a subclass of `StandardError`. ([@koic][]) * [#10421](https://github.com/rubocop/rubocop/issues/10421): Make `Style/DefWithParentheses` aware of endless method definition. ([@koic][]) * [#10401](https://github.com/rubocop/rubocop/issues/10401): Fix a false positive for `Style/HashSyntax` when local variable hash key and hash value are the same. ([@koic][]) -* [#10424](https://github.com/rubocop/rubocop/pull/10424): Fix a false positive for `Security/YamlLoad` when using Ruby 3.1+ (Psych 4). ([@koic][]) -* [#10446](https://github.com/rubocop/rubocop/pull/10446): Lint/RedundantDirGlobSort unset SafeAutoCorrect. ([@friendlyantz][]) +* [#10424](https://github.com/rubocop/rubocop/pull/10424): Fix a false positive for `Security/YAMLLoad` when using Ruby 3.1+ (Psych 4). ([@koic][]) +* [#10446](https://github.com/rubocop/rubocop/pull/10446): `Lint/RedundantDirGlobSort` unset SafeAutoCorrect. ([@friendlyantz][]) * [#10403](https://github.com/rubocop/rubocop/issues/10403): Fix an error for `Style/StringConcatenation` when string concatenation with multiline heredoc text. ([@koic][]) * [#10432](https://github.com/rubocop/rubocop/pull/10432): Fix an error when using regexp with non-encoding option. ([@koic][]) * [#10415](https://github.com/rubocop/rubocop/issues/10415): Fix an error for `Lint/UselessTimes` when using `1.times` with method chain. ([@koic][]) @@ -675,8 +938,8 @@ * [#10357](https://github.com/rubocop/rubocop/pull/10357): Fix a false positive for `Style/HashSyntax` when omitting the value. ([@berkos][]) * [#10335](https://github.com/rubocop/rubocop/issues/10335): Fix a false positive for `Naming/BlockForwarding` when using multiple proc arguments. ([@koic][]) * [#10350](https://github.com/rubocop/rubocop/pull/10350): Fix a false negative for `Lint/IncompatibleIoSelectWithFiberScheduler` when using `IO.select` with the first argument only. ([@koic][]) -* [#10358](https://github.com/rubocop/rubocop/pull/10358): Fix Style/Sample crash on beginless and endless range shuffle indexes. ([@gsamokovarov][]) -* [#10354](https://github.com/rubocop/rubocop/pull/10354): Fix Gemspec/RequiredRubyVersion version matcher when Gem::Requirement.new is used and initialised with multiple requirements. ([@nickpellant][]) +* [#10358](https://github.com/rubocop/rubocop/pull/10358): Fix `Style/Sample` crash on beginless and endless range shuffle indexes. ([@gsamokovarov][]) +* [#10354](https://github.com/rubocop/rubocop/pull/10354): Fix `Gemspec/RequiredRubyVersion` version matcher when Gem::Requirement.new is used and initialised with multiple requirements. ([@nickpellant][]) ### Changes @@ -693,7 +956,7 @@ * [#10333](https://github.com/rubocop/rubocop/pull/10333): Fix an incorrect autocorrect for `Naming/BlockForwarding` using explicit block forwarding without method definition parentheses. ([@koic][]) * [#10321](https://github.com/rubocop/rubocop/issues/10321): Make `Style/MethodDefParentheses` aware of Ruby 3.1's anonymous block forwarding. ([@koic][]) * [#10320](https://github.com/rubocop/rubocop/issues/10320): Fix an incorrect autocorrect for `Style/FileWrite` when using heredoc argument. ([@koic][]) -* [#10319](https://github.com/rubocop/rubocop/issues/10319): Require rubocop-ast 1.15.1 to fix a false positive for `Style/CombinableLoop` when the same method with different arguments and safe navigation. ([@koic][]) +* [#10319](https://github.com/rubocop/rubocop/issues/10319): Require rubocop-ast 1.15.1 to fix a false positive for `Style/CombinableLoops` when the same method with different arguments and safe navigation. ([@koic][]) ## 1.24.0 (2021-12-23) @@ -764,7 +1027,7 @@ * [#10166](https://github.com/rubocop/rubocop/pull/10166): Fix a false positive for `Style/StringLiterals` when using some meta characters (e.g. `'\s'`, `'\z'`) with `EnforcedStyle: double_quotes`. ([@koic][]) * [#10216](https://github.com/rubocop/rubocop/issues/10216): Fix an incorrect autocorrect for `Style/SelectByRegexp` when using `lvar =~ blockvar` in a block. ([@koic][]) -* [#10207](https://github.com/rubocop/rubocop/pull/10207): Fix false positive in Layout/DotPosition when the selector is on the same line as the closing bracket of the receiver. ([@mvz][]) +* [#10207](https://github.com/rubocop/rubocop/pull/10207): Fix false positive in `Layout/DotPosition` when the selector is on the same line as the closing bracket of the receiver. ([@mvz][]) ### Changes @@ -823,7 +1086,7 @@ * [#10078](https://github.com/rubocop/rubocop/pull/10078): Fix `Layout/LineLength` reported length when ignoring directive comments. ([@dvandersluis][]) * [#9934](https://github.com/rubocop/rubocop/issues/9934): Fix configuration loading to not raise an error for an obsolete ruby version that is subsequently overridden. ([@dvandersluis][]) * [#10136](https://github.com/rubocop/rubocop/issues/10136): Update `Lint/AssignmentInCondition` to not consider assignments within blocks in conditions. ([@dvandersluis][]) -* [#9588](https://github.com/rubocop/rubocop/issues/9588): Fix causing a variable to be shadowed from outside the rescue block in the logic of Naming/RescuedExceptionsVariableName. ([@lilisako][]) +* [#9588](https://github.com/rubocop/rubocop/issues/9588): Fix causing a variable to be shadowed from outside the rescue block in the logic of `Naming/RescuedExceptionsVariableName`. ([@lilisako][]) * [#10096](https://github.com/rubocop/rubocop/issues/10096): Fix `Lint/AmbiguousOperatorPrecedence` with `and`/`or` operators. ([@dvandersluis][]) * [#10106](https://github.com/rubocop/rubocop/issues/10106): Fix `Style/RedundantSelf` for pattern matching. ([@dvandersluis][]) * [#10066](https://github.com/rubocop/rubocop/issues/10066): Fix how `MinDigits` is calculated for `Style/NumericLiterals` when generating a configuration file. ([@dvandersluis][]) @@ -866,14 +1129,14 @@ ### Bug fixes -* [#10033](https://github.com/rubocop/rubocop/issues/10033): Fix an incorrect auto-correct for `Style/BlockDelimiters` when there is a comment after the closing brace and using method chanin. ([@koic][]) +* [#10033](https://github.com/rubocop/rubocop/issues/10033): Fix an incorrect auto-correct for `Style/BlockDelimiters` when there is a comment after the closing brace and using method chaining. ([@koic][]) * [#6630](https://github.com/rubocop/rubocop/issues/6630): Updated `Style/CommentAnnotation` to be able to handle multiword keyword phrases. ([@dvandersluis][]) -* [#7836](https://github.com/rubocop/rubocop/issues/7836): Update `Style/BlockDelimeters` to add `begin`...`end` when converting a block containing `rescue` or `ensure` to braces. ([@dvandersluis][]) +* [#7836](https://github.com/rubocop/rubocop/issues/7836): Update `Style/BlockDelimiters` to add `begin`...`end` when converting a block containing `rescue` or `ensure` to braces. ([@dvandersluis][]) * [#10031](https://github.com/rubocop/rubocop/issues/10031): Fix a false positive for `Style/HashExcept` when comparing with hash value. ([@koic][]) ### Changes -* [#10034](https://github.com/rubocop/rubocop/pull/10034): Add `RubyJard` debugger calls to Lint/Debugger/DebuggerMethods. ([@DanielVartanov][]) +* [#10034](https://github.com/rubocop/rubocop/pull/10034): Add `RubyJard` debugger calls to `DebuggerMethods` of `Lint/Debugger`. ([@DanielVartanov][]) * [#10006](https://github.com/rubocop/rubocop/pull/10006): Interpolated string literals are no longer frozen since Ruby 3.0. ([@splattael][]) * [#9328](https://github.com/rubocop/rubocop/issues/9328): Recognize shareable_constant_value magic comment. ([@thearjunmdas][], [@caalberts][]) * [#10036](https://github.com/rubocop/rubocop/issues/10036): Mark `Style/StructInheritance` as unsafe auto-correction. ([@koic][]) @@ -912,7 +1175,7 @@ * [#9975](https://github.com/rubocop/rubocop/issues/9975): Parentheses are always required for `Style/MethodDefParentheses` when a forwarding argument (`...`) is used. ([@dvandersluis][]) * [#9984](https://github.com/rubocop/rubocop/pull/9984): Fix false negatives involving heredocs for `Layout/SpaceBeforeComma`, `Layout/SpaceBeforeComment`, `Layout/SpaceBeforeSemicolon` and `Layout/SpaceInsideParens`. ([@dvandersluis][]) * [#9954](https://github.com/rubocop/rubocop/issues/9954): Fix infinite loop error for `Layout/HashAlignment` when `EnforcedStyle: with_fixed_indentation` is specified for `Layout/ArgumentAlignment`. ([@koic][]) -* [#10002](https://github.com/rubocop/rubocop/issues/10002): Fix an incorrect auto-correct for `Lint/AmbigousRegexpLiteral` when using nested method arguments without parentheses. ([@koic][]) +* [#10002](https://github.com/rubocop/rubocop/issues/10002): Fix an incorrect auto-correct for `Lint/AmbiguousRegexpLiteral` when using nested method arguments without parentheses. ([@koic][]) * [#9952](https://github.com/rubocop/rubocop/pull/9952) [rubocop-rspec#1126](https://github.com/rubocop/rubocop-rspec/issues/1126): Fix `inherit_mode` for deeply nested configuration defined in extensions' default configuration. ([@pirj][]) * [#9957](https://github.com/rubocop/rubocop/issues/9957): Add `WholeWord` configuration to `Naming/InclusiveLanguage`'s `FlaggedTerms` config. ([@dvandersluis][]) * [#9970](https://github.com/rubocop/rubocop/pull/9970): Don't register an offense when sort method has arguments for `Style/RedundantSort` cop. ([@mtsmfm][]) @@ -990,7 +1253,7 @@ * [#7592](https://github.com/rubocop/rubocop/pull/7592): Add cop `Layout/LineEndStringConcatenationIndentation`. ([@jonas054][]) * [#9880](https://github.com/rubocop/rubocop/pull/9880): Fix a false positive for `Style/RegexpLiteral` when using a regexp starts with a blank as a method argument. ([@koic][]) * [#9888](https://github.com/rubocop/rubocop/pull/9888): Fix a false positive for `Layout/ClosingParenthesisIndentation` when using keyword arguments. ([@koic][]) -* [#9886](https://github.com/rubocop/rubocop/pull/9886): Fix indentation in Style/ClassAndModuleChildren. ([@markburns][]) +* [#9886](https://github.com/rubocop/rubocop/pull/9886): Fix indentation in `Style/ClassAndModuleChildren`. ([@markburns][]) ### Changes @@ -1062,7 +1325,7 @@ * [#9749](https://github.com/rubocop/rubocop/issues/9749): Fix autocorrection for `Layout/LineLength` to not move the first argument of an unparenthesized `send` node to the next line, which changes behaviour. ([@dvandersluis][]) * [#9799](https://github.com/rubocop/rubocop/issues/9799): Fix invalid line splitting by `Layout/LineLength` for `send` nodes with heredoc arguments. ([@dvandersluis][]) -* [#9773](https://github.com/rubocop/rubocop/issues/9773): Fix `Style/EmptyLiteral` to not register offenses for `String.new` when `Style/FrozenStringLiteral` is enabled. ([@dvandersluis][]) +* [#9773](https://github.com/rubocop/rubocop/issues/9773): Fix `Style/EmptyLiteral` to not register offenses for `String.new` when `Style/FrozenStringLiteralComment` is enabled. ([@dvandersluis][]) * [#9771](https://github.com/rubocop/rubocop/issues/9771): Change `AllowDSLWriters` to true by default for `Style/TrivialAccessors`. ([@koic][]) * [#9777](https://github.com/rubocop/rubocop/pull/9777): Fix an incorrect auto-correct for `Style/RedundantBegin` when using multi-line `if` in `begin` block. ([@koic][]) * [#9791](https://github.com/rubocop/rubocop/pull/9791): Fix a false negative for `Layout/IndentationWidth` when using `ensure` in `do` ... `end` block. ([@koic][]) @@ -1086,7 +1349,7 @@ ### Bug fixes -* [#9751](https://github.com/rubocop/rubocop/pull/9751): `Style/StringLiteral` doesn't autocorrect global variable interpolation. ([@etiennebarrie][]) +* [#9751](https://github.com/rubocop/rubocop/pull/9751): `Style/StringLiterals` doesn't autocorrect global variable interpolation. ([@etiennebarrie][]) * [#9731](https://github.com/rubocop/rubocop/issues/9731): Fix two autocorrection issues for `Style/NegatedIfElseCondition`. ([@dvandersluis][]) * [#9740](https://github.com/rubocop/rubocop/pull/9740): Fix an incorrect auto-correct for `Style/SingleLineMethods` when defining setter method. ([@koic][]) * [#9757](https://github.com/rubocop/rubocop/pull/9757): Fix a false positive for `Lint/NumberConversion` when `:to_f` is one of multiple method arguments. ([@koic][]) @@ -1095,7 +1358,7 @@ * [#9762](https://github.com/rubocop/rubocop/issues/9762): Update `VariableForce` to be able to handle `case-match` nodes. ([@dvandersluis][]) * [#9729](https://github.com/rubocop/rubocop/issues/9729): Fix an error for `Style/IfUnlessModifier` when variable assignment is used in the branch body of if modifier. ([@koic][]) * [#9750](https://github.com/rubocop/rubocop/issues/9750): Fix an incorrect auto-correct for `Style/SoleNestedConditional` when when using nested `if` within `unless foo == bar`. ([@koic][]) -* [#9751](https://github.com/rubocop/rubocop/pull/9751): `Style/StringLiteral` autocorrects `'\\'` into `"\\"`. ([@etiennebarrie][]) +* [#9751](https://github.com/rubocop/rubocop/pull/9751): `Style/StringLiterals` autocorrects `'\\'` into `"\\"`. ([@etiennebarrie][]) * [#9732](https://github.com/rubocop/rubocop/pull/9732): Support deprecated Socket.gethostbyaddr and Socket.gethostbyname. ([@AndreiEres][]) * [#9713](https://github.com/rubocop/rubocop/issues/9713): Fix autocorrection for block local variables in `Lint/UnusedBlockArgument`. ([@tejasbubane][]) * [#9746](https://github.com/rubocop/rubocop/pull/9746): Fix a false positive for `Lint/UnreachableLoop` when using conditional `next` in a loop. ([@koic][]) @@ -1106,8 +1369,8 @@ * [#7977](https://github.com/rubocop/rubocop/issues/7977): Add `Layout/RedundantLineBreak` cop. ([@jonas054][]) * [#9691](https://github.com/rubocop/rubocop/issues/9691): Add configuration parameter `InspectBlocks` to `Layout/RedundantLineBreak`. ([@jonas054][]) -* [#9684](https://github.com/rubocop/rubocop/issues/9684): Support `IgnoredMethods` option for `Lint/AmbguousBlockAssociation`. ([@gprado][]) -* [#9358](https://github.com/rubocop/rubocop/pull/9358): Support `restrictive_version_specificiers` option in `Bundler/GemComment` cop. ([@RobinDaugherty][]) +* [#9684](https://github.com/rubocop/rubocop/issues/9684): Support `IgnoredMethods` option for `Lint/AmbiguousBlockAssociation`. ([@gprado][]) +* [#9358](https://github.com/rubocop/rubocop/pull/9358): Support `restrictive_version_specifiers` option in `Bundler/GemComment` cop. ([@RobinDaugherty][]) ### Bug fixes @@ -1153,12 +1416,12 @@ * [#9615](https://github.com/rubocop/rubocop/pull/9615): Add new `Style/StringChars` cop. ([@koic][]) * [#9629](https://github.com/rubocop/rubocop/issues/9629): Add `AllowParenthesesInStringInterpolation` configuration to `Style/MethodCallWithArgsParentheses` to allow parenthesized calls in string interpolation. ([@gsamokovarov][]) -* [#9219](https://github.com/rubocop/rubocop/pull/9219): Allow excluding some constants from Style/Documentation. ([@fsateler][]) +* [#9219](https://github.com/rubocop/rubocop/pull/9219): Allow excluding some constants from `Style/Documentation`. ([@fsateler][]) * Add `AllowNil` option for `Lint/SuppressedException` to allow/disallow `rescue nil`. ([@corroded][]) ### Bug fixes -* [#9560](https://github.com/rubocop/rubocop/pull/9560): Fix an error for `Lint/ClassMethodsDefinitions` when defining class methods with `class << self` with comment only body. ([@koic][]) +* [#9560](https://github.com/rubocop/rubocop/pull/9560): Fix an error for `Style/ClassMethodsDefinitions` when defining class methods with `class << self` with comment only body. ([@koic][]) * [#9551](https://github.com/rubocop/rubocop/issues/9551): Fix a false positive for `Style/UnlessLogicalOperators` when using `||` operator and invoked method name includes "or" in the conditional branch. ([@koic][]) * [#9620](https://github.com/rubocop/rubocop/pull/9620): Allow parentheses in operator methods calls for `Style/MethodCallWithArgsParentheses` `EnforcedStyle: omit_parentheses`. ([@gsamokovarov][]) * [#9622](https://github.com/rubocop/rubocop/issues/9622): Fixed `Style/BisectedAttrAccessor` autocorrection to handle multiple bisected attrs in the same macro. ([@dvandersluis][]) @@ -1182,7 +1445,7 @@ ### Changes -* [#9487](https://github.com/rubocop/rubocop/issues/9487): Mark Naming/MemoizedInstanceVariableName as unsafe. ([@marcandre][]) +* [#9487](https://github.com/rubocop/rubocop/issues/9487): Mark `Naming/MemoizedInstanceVariableName` as unsafe. ([@marcandre][]) * [#9601](https://github.com/rubocop/rubocop/issues/9601): Make `Style/RedundantBegin` aware of redundant `begin`/`end` blocks around memoization. ([@koic][]) * [#9617](https://github.com/rubocop/rubocop/issues/9617): Disable suggested extensions when using the `--stdin` option. ([@dvandersluis][]) @@ -1260,7 +1523,7 @@ ### Changes * [#9437](https://github.com/rubocop/rubocop/issues/9437): Improve offense message when there is an allowed range of empty lines. ([@dvandersluis][]) -* [#9476](https://github.com/rubocop/rubocop/pull/9476): Mark `Style/IfWithBooleanLiteralBranche` as unsafe auto-correction. ([@koic][]) +* [#9476](https://github.com/rubocop/rubocop/pull/9476): Mark `Style/IfWithBooleanLiteralBranches` as unsafe auto-correction. ([@koic][]) ## 1.9.0 (2021-01-28) @@ -1307,7 +1570,7 @@ * [#9342](https://github.com/rubocop/rubocop/issues/9342): Fix an error for `Lint/RedundantDirGlobSort` when using `collection.sort`. ([@koic][]) * [#9304](https://github.com/rubocop/rubocop/issues/9304): Do not register an offense for `Style/ExplicitBlockArgument` when the `yield` arguments are not an exact match with the block arguments. ([@dvandersluis][]) -* [#8281](https://github.com/rubocop/rubocop/issues/8281): Fix Style/WhileUntilModifier handling comments and assignment when correcting to modifier form. ([@Darhazer][]) +* [#8281](https://github.com/rubocop/rubocop/issues/8281): Fix `Style/WhileUntilModifier` handling comments and assignment when correcting to modifier form. ([@Darhazer][]) * [#8229](https://github.com/rubocop/rubocop/issues/8229): Fix faulty calculation in UncommunicativeName. ([@ohbarye][]) * [#9350](https://github.com/rubocop/rubocop/pull/9350): Wrap in parens before replacing `unless` with `if` and `!`. ([@magneland][]) * [#9356](https://github.com/rubocop/rubocop/pull/9356): Fix duplicate extension cop versions when using `rubocop -V`. ([@koic][]) @@ -1361,17 +1624,17 @@ * [#9232](https://github.com/rubocop/rubocop/pull/9232): Fix `Style/SymbolProc` registering wrong offense when using a symbol numbered block argument greater than 1, i.e. `[[1, 2]].map { _2.succ }`. ([@tdeo][]) * [#9274](https://github.com/rubocop/rubocop/issues/9274): Fix error in `Metrics/ClassLength` when the class only contains comments. ([@dvandersluis][]) -* [#9213](https://github.com/rubocop/rubocop/issues/9213): Fix a false positive for `Style/RedanduntFreeze` when using `Array#*`. ([@koic][]) +* [#9213](https://github.com/rubocop/rubocop/issues/9213): Fix a false positive for `Style/RedundantFreeze` when using `Array#*`. ([@koic][]) * [#9279](https://github.com/rubocop/rubocop/pull/9279): Add support for endless methods to `Style/MethodCallWithArgsParentheses`. ([@dvandersluis][]) * [#9245](https://github.com/rubocop/rubocop/issues/9245): Fix `Lint/AmbiguousRegexpLiteral` when given a `match_with_lvasgn` node. ([@dvandersluis][]) * [#9276](https://github.com/rubocop/rubocop/pull/9276): Add support for endless methods to `Style/SingleLineMethods`. ([@dvandersluis][]) -* [#9225](https://github.com/rubocop/rubocop/issues/9225): Fix Style/LambdaCall ignoring further offenses after opposite style is detected. ([@sswander][]) +* [#9225](https://github.com/rubocop/rubocop/issues/9225): Fix `Style/LambdaCall` ignoring further offenses after opposite style is detected. ([@sswander][]) * [#9234](https://github.com/rubocop/rubocop/issues/9234): Fix the error for `Style/KeywordParametersOrder` and make it aware of block keyword parameters. ([@koic][]) * [#8938](https://github.com/rubocop/rubocop/pull/8938): Fix some ConfigurableEnforcedStyle cops to output `Exclude` file lists in `--auto-gen-config` runs. ([@h-lame][]) * [#9257](https://github.com/rubocop/rubocop/issues/9257): Fix false positive for `Style/SymbolProc` when the block uses a variable from outside the block. ([@dvandersluis][]) * [#9251](https://github.com/rubocop/rubocop/issues/9251): Fix extracted cop warning when the extension is loaded using `--require`. ([@dvandersluis][]) * [#9244](https://github.com/rubocop/rubocop/issues/9244): When a cop defined in an extension is explicitly enabled, ensure that it remains enabled. ([@dvandersluis][]) -* [#8046](https://github.com/rubocop/rubocop/issues/8046): Fix an error for `Layout/HeredocArgumentClosingParenthesis` when there is an argument between a heredoc argument and the closing paretheses. ([@koic][]) +* [#8046](https://github.com/rubocop/rubocop/issues/8046): Fix an error for `Layout/HeredocArgumentClosingParenthesis` when there is an argument between a heredoc argument and the closing parentheses. ([@koic][]) * [#9261](https://github.com/rubocop/rubocop/pull/9261): Fix an incorrect auto-correct for `Style/MultilineWhenThen` when one line for multiple candidate values of `when` statement. ([@makicamel][]) * [#9258](https://github.com/rubocop/rubocop/pull/9258): Fix calculation of cop department for nested departments. ([@mvz][]) * [#9277](https://github.com/rubocop/rubocop/pull/9277): Fix `Layout/EmptyLineBetweenDefs` error with endless method definitions. ([@dvandersluis][]) @@ -1433,7 +1696,7 @@ ### Changes -* [#9080](https://github.com/rubocop/rubocop/issues/9080): Make `Lint/ShadowingOuterVariable` aware of `Ractor`. ([@tejasbubane][]) +* [#9080](https://github.com/rubocop/rubocop/issues/9080): Make `Lint/ShadowingOuterLocalVariable` aware of `Ractor`. ([@tejasbubane][]) * [#9102](https://github.com/rubocop/rubocop/pull/9102): Relax regexp_parser requirement. ([@marcandre][]) ## 1.5.1 (2020-12-02) @@ -1458,7 +1721,7 @@ * [#8820](https://github.com/rubocop/rubocop/issues/8820): Fixes `IfWithSemicolon` autocorrection when `elsif` is present. ([@adrian-rivera][], [@dvandersluis][]) * [#9113](https://github.com/rubocop/rubocop/pull/9113): Fix a false positive for `Style/MethodCallWithoutArgsParentheses` when assigning to a default argument with the same name. ([@koic][]) -* [#9115](https://github.com/rubocop/rubocop/issues/9115): Fix a false positive for `Style/FirstArgumentIndentation` when argument has expected indent width and the method is preceded by splat for `EnforcedStyle: consistent_relative_to_receiver`. ([@koic][]) +* [#9115](https://github.com/rubocop/rubocop/issues/9115): Fix a false positive for `Layout/FirstArgumentIndentation` when argument has expected indent width and the method is preceded by splat for `EnforcedStyle: consistent_relative_to_receiver`. ([@koic][]) * [#9128](https://github.com/rubocop/rubocop/issues/9128): Fix an incorrect auto-correct for `Style/ClassAndModuleChildren` when namespace is defined as a class in the same file. ([@koic][]) * [#9105](https://github.com/rubocop/rubocop/issues/9105): Fix an incorrect auto-correct for `Style/RedundantCondition` when using operator method in `else`. ([@koic][]) * [#9096](https://github.com/rubocop/rubocop/pull/9096): Fix #9095 use merged_config instead of config for pending new cop check. ([@ThomasKoppensteiner][]) @@ -1481,7 +1744,7 @@ ### Bug fixes * [#9083](https://github.com/rubocop/rubocop/pull/9083): Fix `Style/RedundantArgument` cop raising offense for more than one argument. ([@tejasbubane][]) -* [#9089](https://github.com/rubocop/rubocop/issues/9089): Fix an incorrect auto-correct for `Style/FormatString` when using springf with second argument that uses an operator. ([@koic][]) +* [#9089](https://github.com/rubocop/rubocop/issues/9089): Fix an incorrect auto-correct for `Style/FormatString` when using sprintf with second argument that uses an operator. ([@koic][]) * [#7670](https://github.com/rubocop/rubocop/issues/7670): Handle offenses inside heredocs for `-a --disable-uncorrectable`. ([@jonas054][]) * [#9070](https://github.com/rubocop/rubocop/issues/9070): Fix `Lint/UnmodifiedReduceAccumulator` error when the block does not have enough arguments. ([@dvandersluis][]) @@ -1626,7 +1889,7 @@ * [#8917](https://github.com/rubocop/rubocop/issues/8917): Fix rubocop comment directives handling of cops with multiple levels in department name. ([@fatkodima][]) * [#8918](https://github.com/rubocop/rubocop/issues/8918): Fix a false positives for `Bundler/DuplicatedGem` when a gem conditionally duplicated within `if-elsif` or `case-when` statements. ([@fatkodima][]) * [#8933](https://github.com/rubocop/rubocop/pull/8933): Fix an error for `Layout/EmptyLinesAroundAccessModifier` when the first line is a comment. ([@matthieugendreau][]) -* [#8954](https://github.com/rubocop/rubocop/pull/8954): Fix autocorrection for Style/RedundantRegexpCharacterClass with %r. ([@ysakasin][]) +* [#8954](https://github.com/rubocop/rubocop/pull/8954): Fix autocorrection for `Style/RedundantRegexpCharacterClass` with %r. ([@ysakasin][]) ### Changes @@ -1644,7 +1907,7 @@ ### Bug fixes -* [#8892](https://github.com/rubocop/rubocop/issues/8892): Fix an error for `Style/StringConcatenation` when correcting nested concatenable parts. ([@fatkodima][]) +* [#8892](https://github.com/rubocop/rubocop/issues/8892): Fix an error for `Style/StringConcatenation` when correcting nested concatenatable parts. ([@fatkodima][]) * [#8781](https://github.com/rubocop/rubocop/issues/8781): Fix handling of comments in `Style/SafeNavigation` autocorrection. ([@dvandersluis][]) * [#8907](https://github.com/rubocop/rubocop/pull/8907): Fix an incorrect auto-correct for `Layout/ClassStructure` when heredoc constant is defined after public method. ([@koic][]) * [#8889](https://github.com/rubocop/rubocop/pull/8889): Cops can use new `after_` callbacks (only for nodes that may have children nodes, like `:send` and unlike `:sym`). ([@marcandre][]) @@ -1656,7 +1919,7 @@ * [#8882](https://github.com/rubocop/rubocop/pull/8882): **(Potentially breaking)** RuboCop assumes that Cop classes do not define new `on_` methods at runtime (e.g. via `extend` in `initialize`). ([@marcandre][]) * [#7966](https://github.com/rubocop/rubocop/issues/7966): **(Breaking)** Enable all pending cops for RuboCop 1.0. ([@koic][]) -* [#8490](https://github.com/rubocop/rubocop/pull/8490): **(Breaking)** Change logic for cop department name computation. Cops inside deep namespaces (5 or more levels deep) now belong to departments with names that are calculated by joining module names starting from the third one with slashes as separators. For example, cop `Rubocop::Cop::Foo::Bar::Baz` now belongs to `Foo/Bar` department (previously it was `Bar`). ([@dsavochkin][]) +* [#8490](https://github.com/rubocop/rubocop/pull/8490): **(Breaking)** Change logic for cop department name computation. Cops inside deep namespaces (5 or more levels deep) now belong to departments with names that are calculated by joining module names starting from the third one with slashes as separators. For example, cop `RuboCop::Cop::Foo::Bar::Baz` now belongs to `Foo/Bar` department (previously it was `Bar`). ([@dsavochkin][]) * [#8692](https://github.com/rubocop/rubocop/pull/8692): Default changed to disallow `Layout/TrailingWhitespace` in heredoc. ([@marcandre][]) * [#8894](https://github.com/rubocop/rubocop/issues/8894): Make `Security/Open` aware of `URI.open`. ([@koic][]) * [#8901](https://github.com/rubocop/rubocop/issues/8901): Fix false positive for `Naming/BinaryOperatorParameterName` when defining `=~`. ([@zajn][]) @@ -1785,7 +2048,7 @@ * [#8654](https://github.com/rubocop/rubocop/pull/8654): Fix a false positive for `Style/SafeNavigation` when checking `foo&.empty?` in a conditional. ([@koic][]) * [#8660](https://github.com/rubocop/rubocop/pull/8660): Fix a false positive for `Style/ClassAndModuleChildren` when using cbase module name. ([@koic][]) * [#8664](https://github.com/rubocop/rubocop/issues/8664): Fix a false positive for `Naming/BinaryOperatorParameterName` when naming multibyte character method name. ([@koic][]) -* [#8604](https://github.com/rubocop/rubocop/issues/8604): Fix a false positive for `Bundler/DuplicatedGem` when gem is duplciated in condition. ([@tejasbubane][]) +* [#8604](https://github.com/rubocop/rubocop/issues/8604): Fix a false positive for `Bundler/DuplicatedGem` when gem is duplicated in condition. ([@tejasbubane][]) * [#8671](https://github.com/rubocop/rubocop/issues/8671): Fix an error for `Style/ExplicitBlockArgument` when using safe navigation method call. ([@koic][]) * [#8681](https://github.com/rubocop/rubocop/pull/8681): Fix an error for `Style/HashAsLastArrayItem` with `no_braces` for empty hash. ([@fsateler][]) * [#8682](https://github.com/rubocop/rubocop/pull/8682): Fix a positive for `Style/HashTransformKeys` and `Style/HashTransformValues` when the `each_with_object` hash is used in the transformed key or value. ([@eugeneius][]) @@ -2076,7 +2339,7 @@ ### Changes -* [#8056](https://github.com/rubocop/rubocop/pull/8056): **(Breaking)** Remove support for unindent/active_support/powerpack from `Layout/HeredocIndentation`, so it only recommends using squiggy heredoc. ([@bquorning][]) +* [#8056](https://github.com/rubocop/rubocop/pull/8056): **(Breaking)** Remove support for unindent/active_support/powerpack from `Layout/HeredocIndentation`, so it only recommends using squiggly heredoc. ([@bquorning][]) ## 0.84.0 (2020-05-21) @@ -2092,7 +2355,7 @@ * [#7953](https://github.com/rubocop/rubocop/issues/7953): Fix an error for `Lint/AmbiguousOperator` when a method with no arguments is used in advance. ([@koic][]) * [#7962](https://github.com/rubocop/rubocop/issues/7962): Fix a false positive for `Lint/ParenthesesAsGroupedExpression` when heredoc has a space between the same string as the method name and `(`. ([@koic][]) * [#7967](https://github.com/rubocop/rubocop/pull/7967): `Style/SlicingWithRange` cop now supports any expression as its first index. ([@zverok][]) -* [#7972](https://github.com/rubocop/rubocop/issues/7972): Fix an incorrect autocrrect for `Style/HashSyntax` when using a return value uses `return`. ([@koic][]) +* [#7972](https://github.com/rubocop/rubocop/issues/7972): Fix an incorrect autocorrect for `Style/HashSyntax` when using a return value uses `return`. ([@koic][]) * [#7886](https://github.com/rubocop/rubocop/issues/7886): Fix a bug in `AllowComments` logic in `Lint/SuppressedException`. ([@jonas054][]) * [#7991](https://github.com/rubocop/rubocop/issues/7991): Fix an error for `Layout/EmptyLinesAroundAttributeAccessor` when attribute method is method chained. ([@koic][]) * [#7993](https://github.com/rubocop/rubocop/issues/7993): Fix a false positive for `Migration/DepartmentName` when a disable comment contains an unexpected character for department name. ([@koic][]) @@ -2102,7 +2365,7 @@ * [#7952](https://github.com/rubocop/rubocop/pull/7952): **(Breaking)** Change the max line length of `Layout/LineLength` to 120 by default. ([@koic][]) * [#7959](https://github.com/rubocop/rubocop/pull/7959): Change enforced style to conditionals for `Style/AndOr`. ([@koic][]) -* [#7985](https://github.com/rubocop/rubocop/pull/7985): Add `EnforcedStyle` for `Style/DoubleNegation` cop and allow double nagation in contexts that use boolean as a return value. ([@koic][]) +* [#7985](https://github.com/rubocop/rubocop/pull/7985): Add `EnforcedStyle` for `Style/DoubleNegation` cop and allow double negation in contexts that use boolean as a return value. ([@koic][]) ## 0.83.0 (2020-05-11) @@ -2608,7 +2871,7 @@ * [#6902](https://github.com/rubocop/rubocop/issues/6902): Fix a bug where `Naming/RescuedExceptionsVariableName` would handle an only first rescue for multiple rescue groups. ([@tatsuyafw][]) * [#6860](https://github.com/rubocop/rubocop/issues/6860): Prevent auto-correct conflict of `Style/InverseMethods` and `Style/Not`. ([@hoshinotsuyoshi][]) * [#6935](https://github.com/rubocop/rubocop/issues/6935): `Layout/AccessModifierIndentation` should ignore access modifiers that apply to specific methods. ([@deivid-rodriguez][]) -* [#6956](https://github.com/rubocop/rubocop/issues/6956): Prevent auto-correct confliction of `Lint/Lambda` and `Lint/UnusedBlockArgument`. ([@koic][]) +* [#6956](https://github.com/rubocop/rubocop/issues/6956): Prevent auto-correct conflict of `Lint/Lambda` and `Lint/UnusedBlockArgument`. ([@koic][]) * [#6915](https://github.com/rubocop/rubocop/issues/6915): Fix false positive in `Style/SafeNavigation` when a modifier if is safe guarding a method call being passed to `break`, `fail`, `next`, `raise`, `return`, `throw`, and `yield`. ([@rrosenblum][]) * [#6822](https://github.com/rubocop/rubocop/issues/6822): Fix Lint/LiteralInInterpolation auto-correction for single quotes. ([@hoshinotsuyoshi][]) * [#6985](https://github.com/rubocop/rubocop/issues/6985): Fix an incorrect auto-correct for `Lint/LiteralInInterpolation` if contains array percent literal. ([@yakout][]) @@ -2784,7 +3047,7 @@ ### Bug fixes * [#6627](https://github.com/rubocop/rubocop/pull/6627): Fix handling of hashes in trailing comma. ([@abrom][]) -* [#6638](https://github.com/rubocop/rubocop/pull/6638): Fix `Rails/LinkToBlank` detection to allow `rel: 'noreferer`. ([@fwininger][]) +* [#6638](https://github.com/rubocop/rubocop/pull/6638): Fix `Rails/LinkToBlank` detection to allow `rel: 'noreferrer`. ([@fwininger][]) * [#6623](https://github.com/rubocop/rubocop/pull/6623): Fix heredoc detection in trailing comma. ([@palkan][]) * [#6100](https://github.com/rubocop/rubocop/issues/6100): Fix a false positive in `Naming/ConstantName` cop when rhs is a conditional expression. ([@tatsuyafw][]) * [#6526](https://github.com/rubocop/rubocop/issues/6526): Fix a wrong line highlight in `Lint/ShadowedException` cop. ([@tatsuyafw][]) @@ -2913,14 +3176,14 @@ ### New features -* Update `Style/MethodCallWithoutArgsParentheses` to highlight the closing parentheses in additition to the opening parentheses. ([@rrosenblum][]) +* Update `Style/MethodCallWithoutArgsParentheses` to highlight the closing parentheses in addition to the opening parentheses. ([@rrosenblum][]) ### Bug fixes * [#6266](https://github.com/rubocop/rubocop/issues/6266): Fix a false positive for `Rails/HasManyOrHasOneDependent` when using associations of Active Resource. ([@tejasbubane][], [@koic][]) * [#6296](https://github.com/rubocop/rubocop/issues/6296): Fix an auto-correct error for `Style/For` when setting `EnforcedStyle: each` and `for` dose not have `do` or semicolon. ([@autopp][]) * [#6300](https://github.com/rubocop/rubocop/pull/6300): Fix a false positive for `Layout/EmptyLineAfterGuardClause` when guard clause including heredoc. ([@koic][]) -* [#6287](https://github.com/rubocop/rubocop/pull/6287): Fix `AllowURI` option for `Metrics/LineLength` cop with disabled `Layut/Tab` cop. ([@AlexWayfer][]) +* [#6287](https://github.com/rubocop/rubocop/pull/6287): Fix `AllowURI` option for `Metrics/LineLength` cop with disabled `Layout/Tab` cop. ([@AlexWayfer][]) * [#5338](https://github.com/rubocop/rubocop/pull/5338): Move checking of class- and module defining blocks from `Metrics/BlockLength` into the respective length cops. ([@drenmi][]) * [#2841](https://github.com/rubocop/rubocop/pull/2841): Fix `Style/ZeroLengthPredicate` false positives when inspecting `Tempfile`, `StringIO`, and `File::Stat` objects. ([@drenmi][]) * [#6305](https://github.com/rubocop/rubocop/pull/6305): Fix infinite loop for `Layout/EmptyLinesAroundAccessModifier` and `Layout/EmptyLinesAroundAccessModifier` when specifying a superclass that breaks the line. ([@koic][]) @@ -3129,7 +3392,7 @@ * [#4136](https://github.com/rubocop/rubocop/issues/4136): Allow more robust `Layout/ClosingParenthesisIndentation` detection including method chaining. ([@jfelchner][]) * [#5699](https://github.com/rubocop/rubocop/pull/5699): Add `consistent_relative_to_receiver` style option to `Layout/FirstParameterIndentation`. ([@jfelchner][]) * [#5821](https://github.com/rubocop/rubocop/pull/5821): Support `AR::Migration#up_only` for `Rails/ReversibleMigration` cop. ([@koic][]) -* [#5800](https://github.com/rubocop/rubocop/issues/5800): Don't show a stracktrace for invalid command-line params. ([@shanecav84][]) +* [#5800](https://github.com/rubocop/rubocop/issues/5800): Don't show a stacktrace for invalid command-line params. ([@shanecav84][]) * [#5845](https://github.com/rubocop/rubocop/pull/5845): Add new `Lint/ErbNewArguments` cop. ([@koic][]) * [#5871](https://github.com/rubocop/rubocop/pull/5871): Add new `Lint/SplatKeywordArguments` cop. ([@koic][]) * [#4247](https://github.com/rubocop/rubocop/issues/4247): Remove hard-coded file patterns and use only `Include`, `Exclude` and the new `RubyInterpreters` parameters for file selection. ([@jonas054][]) @@ -3151,7 +3414,7 @@ * [#5862](https://github.com/rubocop/rubocop/issues/5862): Fix an incorrect auto-correct for `Lint/LiteralInInterpolation` if contains numbers. ([@koic][]) * [#5868](https://github.com/rubocop/rubocop/pull/5868): Fix `Rails/CreateTableWithTimestamps` when using hash options. ([@wata727][]) * [#5708](https://github.com/rubocop/rubocop/issues/5708): Fix exception in `Lint/UnneededCopEnableDirective` for instruction '# rubocop:enable **all**'. ([@balbesina][]) -* Fix auto-correction of `Rails/HttpPositionalArgumnets` to use `session` instead of `header`. ([@rrosenblum][]) +* Fix auto-correction of `Rails/HttpPositionalArguments` to use `session` instead of `header`. ([@rrosenblum][]) ### Changes @@ -3181,7 +3444,7 @@ * [#5738](https://github.com/rubocop/rubocop/issues/5738): Make `Rails/HttpStatus` ignoring hash order to fix false negative. ([@pocke][]) * [#5720](https://github.com/rubocop/rubocop/pull/5720): Fix false positive for `Style/EmptyLineAfterGuardClause` when guard clause is after heredoc. ([@koic][]) * [#5760](https://github.com/rubocop/rubocop/pull/5760): Fix incorrect offense location for `Style/EmptyLineAfterGuardClause` when guard clause is after heredoc argument. ([@koic][]) -* [#5764](https://github.com/rubocop/rubocop/pull/5764): Fix `Style/Unpackfirst` false positive of `unpack('h*').take(1)`. ([@parkerfinch][]) +* [#5764](https://github.com/rubocop/rubocop/pull/5764): Fix `Style/UnpackFirst` false positive of `unpack('h*').take(1)`. ([@parkerfinch][]) * [#5766](https://github.com/rubocop/rubocop/issues/5766): Update `Style/FrozenStringLiteralComment` auto-correction to insert a new line between the comment and the code. ([@rrosenblum][]) * [#5551](https://github.com/rubocop/rubocop/issues/5551): Fix `Lint/Void` not detecting void context in blocks with single expression. ([@Darhazer][]) @@ -3309,7 +3572,7 @@ ### Changes * [#5589](https://github.com/rubocop/rubocop/issues/5589): Remove `Performance/HashEachMethods` cop as it no longer provides a performance benefit. ([@urbanautomaton][]) -* [#3394](https://github.com/rubocop/rubocop/issues/3394): Remove `Style/TrailingCommmaInLiteral` in favor of two new cops. ([@garettarrowood][]) +* [#3394](https://github.com/rubocop/rubocop/issues/3394): Remove `Style/TrailingCommaInLiteral` in favor of two new cops. ([@garettarrowood][]) * Rename `Lint/UnneededDisable` to `Lint/UnneededCopDisableDirective`. ([@garettarrowood][]) * [#5365](https://github.com/rubocop/rubocop/pull/5365): Add `*.gemfile` to Bundler cop target. ([@sue445][]) * [#4477](https://github.com/rubocop/rubocop/issues/4477): Warn when user configuration overrides other user configuration. ([@jonas054][]) @@ -3371,7 +3634,7 @@ * [#4811](https://github.com/rubocop/rubocop/issues/4811): Add new `Layout/SpaceInsideReferenceBrackets` cop. ([@garettarrowood][]) * [#4811](https://github.com/rubocop/rubocop/issues/4811): Add new `Layout/SpaceInsideArrayLiteralBrackets` cop. ([@garettarrowood][]) * [#4252](https://github.com/rubocop/rubocop/issues/4252): Add new `Style/TrailingBodyOnMethodDefinition` cop. ([@garettarrowood][]) -* Add new `Style/TrailingMethodEndStatment` cop. ([@garettarrowood][]) +* Add new `Style/TrailingMethodEndStatement` cop. ([@garettarrowood][]) * [#5074](https://github.com/rubocop/rubocop/issues/5074): Add Layout/EmptyLinesAroundArguments cop. ([@garettarrowood][]) * [#4650](https://github.com/rubocop/rubocop/issues/4650): Add new `Style/StringHashKeys` cop. ([@donjar][]) * [#1583](https://github.com/rubocop/rubocop/issues/1583): Add a quiet formatter. ([@drenmi][]) @@ -3412,7 +3675,7 @@ * [#4885](https://github.com/rubocop/rubocop/issues/4885): Fix false offense detected by `Style/MixinUsage` cop. ([@koic][]) * [#3363](https://github.com/rubocop/rubocop/pull/3363): Fix `Style/EmptyElse` auto-correction removes comments from branches. ([@dpostorivo][]) * [#5025](https://github.com/rubocop/rubocop/issues/5025): Fix error with Layout/MultilineMethodCallIndentation cop and lambda.(...). ([@dpostorivo][]) -* [#4781](https://github.com/rubocop/rubocop/issues/4781): Prevent `Style/UnneededPercentQ` from breaking on strings that are concated with backslash. ([@pocke][]) +* [#4781](https://github.com/rubocop/rubocop/issues/4781): Prevent `Style/UnneededPercentQ` from breaking on strings that are concatenated with backslash. ([@pocke][]) * [#4363](https://github.com/rubocop/rubocop/issues/4363): Fix `Style/PercentLiteralDelimiters` incorrectly automatically modifies escaped percent literal delimiter. ([@koic][]) * [#5053](https://github.com/rubocop/rubocop/issues/5053): Fix `Naming/ConstantName` false offense on assigning to a nonoffensive assignment. ([@garettarrowood][]) * [#5019](https://github.com/rubocop/rubocop/pull/5019): Fix auto-correct for `Style/HashSyntax` cop when hash is used as unspaced argument. ([@drenmi][]) @@ -3589,7 +3852,7 @@ * [#4550](https://github.com/rubocop/rubocop/pull/4550): Mark `RuboCop::CLI#run` as a public API. ([@yujinakayama][]) * [#4551](https://github.com/rubocop/rubocop/pull/4551): Make `Performance/Caller` aware of `caller_locations`. ([@pocke][]) * [#4547](https://github.com/rubocop/rubocop/pull/4547): Rename `Style/HeredocDelimiters` to `Style/HeredocDelimiterNaming`. ([@drenmi][]) -* [#4157](https://github.com/rubocop/rubocop/issues/4157): Enhance offense message for `Style/RedudantReturn` cop. ([@gohdaniel15][]) +* [#4157](https://github.com/rubocop/rubocop/issues/4157): Enhance offense message for `Style/RedundantReturn` cop. ([@gohdaniel15][]) * [#4521](https://github.com/rubocop/rubocop/issues/4521): Move naming related cops into their own `Naming` department. ([@drenmi][]) * [#4600](https://github.com/rubocop/rubocop/pull/4600): Make `Style/RedundantSelf` aware of arguments of a block. ([@Envek][]) * [#4658](https://github.com/rubocop/rubocop/issues/4658): Disable auto-correction for `Performance/TimesMap` by default. ([@Envek][]) @@ -3659,7 +3922,7 @@ * [#4264](https://github.com/rubocop/rubocop/issues/4264): Prevent `Rails/SaveBang` from blowing up when using the assigned variable in a hash. ([@drenmi][]) * [#4310](https://github.com/rubocop/rubocop/pull/4310): Treat paths containing invalid byte sequences as non-matches. ([@mclark][]) * [#4063](https://github.com/rubocop/rubocop/issues/4063): Fix Rails/ReversibleMigration misdetection. ([@gprado][]) -* [#4339](https://github.com/rubocop/rubocop/pull/4339): Fix false positive in `Security/Eval` cop for multiline string lietral. ([@pocke][]) +* [#4339](https://github.com/rubocop/rubocop/pull/4339): Fix false positive in `Security/Eval` cop for multiline string literal. ([@pocke][]) * [#4339](https://github.com/rubocop/rubocop/pull/4339): Fix false negative in `Security/Eval` cop for `Binding#eval`. ([@pocke][]) * [#4327](https://github.com/rubocop/rubocop/issues/4327): Prevent `Layout/SpaceInsidePercentLiteralDelimiters` from registering offenses on execute-strings. ([@drenmi][]) * [#4371](https://github.com/rubocop/rubocop/issues/4371): Prevent `Style/MethodName` from complaining about unary operator definitions. ([@drenmi][]) @@ -3688,7 +3951,7 @@ * [#4197](https://github.com/rubocop/rubocop/pull/4197): Fix false positive in `Style/RedundantSelf` cop with parallel assignment. ([@drenmi][]) * [#4199](https://github.com/rubocop/rubocop/issues/4199): Fix incorrect auto correction in `Style/SymbolArray` and `Style/WordArray` cop. ([@pocke][]) * [#4218](https://github.com/rubocop/rubocop/pull/4218): Make `Lint/NestedMethodDefinition` aware of class shovel scope. ([@drenmi][]) -* [#4198](https://github.com/rubocop/rubocop/pull/4198): Make `Lint/AmbguousBlockAssociation` aware of operator methods. ([@drenmi][]) +* [#4198](https://github.com/rubocop/rubocop/pull/4198): Make `Lint/AmbiguousBlockAssociation` aware of operator methods. ([@drenmi][]) * [#4152](https://github.com/rubocop/rubocop/pull/4152): Make `Style/MethodCallWithArgsParentheses` not require parens on setter methods. ([@drenmi][]) * [#4226](https://github.com/rubocop/rubocop/pull/4226): Show in `--help` output that `--stdin` takes a file name argument. ([@jonas054][]) * [#4217](https://github.com/rubocop/rubocop/pull/4217): Fix false positive in `Rails/FilePath` cop with non string argument. ([@soutaro][]) @@ -3705,7 +3968,7 @@ * [#4083](https://github.com/rubocop/rubocop/pull/4083): Add new configuration `NumberOfEmptyLines` for `Style/EmptyLineBetweenDefs`. ([@dorian][]) * [#4045](https://github.com/rubocop/rubocop/pull/4045): Add new configuration `Strict` for `Style/NumericLiteral` to make the change to this cop in 0.47.0 configurable. ([@iGEL][]) * [#4005](https://github.com/rubocop/rubocop/issues/4005): Add new `AllCops/EnabledByDefault` option. ([@betesh][]) -* [#3893](https://github.com/rubocop/rubocop/issues/3893): Add a new configuration, `IncludeActiveSupportAliases`, to `Performance/DoublStartEndWith`. This configuration will check for ActiveSupport's `starts_with?` and `ends_with?`. ([@rrosenblum][]) +* [#3893](https://github.com/rubocop/rubocop/issues/3893): Add a new configuration, `IncludeActiveSupportAliases`, to `Performance/DoubleStartEndWith`. This configuration will check for ActiveSupport's `starts_with?` and `ends_with?`. ([@rrosenblum][]) * [#3889](https://github.com/rubocop/rubocop/pull/3889): Add new `Style/EmptyLineAfterMagicComment` cop. ([@backus][]) * [#3800](https://github.com/rubocop/rubocop/issues/3800): Make `Style/EndOfLine` configurable with `lf`, `crlf`, and `native` (default) styles. ([@jonas054][]) * [#3936](https://github.com/rubocop/rubocop/issues/3936): Add new `Style/MixinGrouping` cop. ([@drenmi][]) @@ -3831,7 +4094,7 @@ * [#3775](https://github.com/rubocop/rubocop/pull/3775): Avoid crash in `Style/HashSyntax` cop with an empty hash. ([@pocke][]) * [#3783](https://github.com/rubocop/rubocop/pull/3783): Maintain parentheses in `Rails/HttpPositionalArguments` when methods are defined with them. ([@kevindew][]) * [#3786](https://github.com/rubocop/rubocop/pull/3786): Avoid crash `Style/ConditionalAssignment` cop with mass assign method. ([@pocke][]) -* [#3749](https://github.com/rubocop/rubocop/pull/3749): Detect corner case of `Style/NumericLitterals`. ([@kamaradclimber][]) +* [#3749](https://github.com/rubocop/rubocop/pull/3749): Detect corner case of `Style/NumericLiterals`. ([@kamaradclimber][]) * [#3788](https://github.com/rubocop/rubocop/pull/3788): Prevent bad auto-correct in `Style/Next` when block has nested conditionals. ([@drenmi][]) * [#3807](https://github.com/rubocop/rubocop/pull/3807): Prevent `Style/Documentation` and `Style/DocumentationMethod` from mistaking RuboCop directives for class documentation. ([@drenmi][]) * [#3815](https://github.com/rubocop/rubocop/pull/3815): Fix false positive in `Style/IdenticalConditionalBranches` cop when branches have same line at leading. ([@pocke][]) @@ -3961,7 +4224,7 @@ * [#3577](https://github.com/rubocop/rubocop/issues/3577): Fix `Style/RaiseArgs` not allowing compact raise with splatted args. ([@savef][]) * [#3578](https://github.com/rubocop/rubocop/issues/3578): Fix safe navigation method call counting in `Metrics/AbcSize`. ([@savef][]) * [#3592](https://github.com/rubocop/rubocop/issues/3592): Fix `Style/RedundantParentheses` for indexing with literals. ([@thegedge][]) -* [#3597](https://github.com/rubocop/rubocop/issues/3597): Fix the auto-correct of `Performance/CaseWhenSplat` when trying to rearange splat expanded variables to the end of a when condition. ([@rrosenblum][]) +* [#3597](https://github.com/rubocop/rubocop/issues/3597): Fix the auto-correct of `Performance/CaseWhenSplat` when trying to rearrange splat expanded variables to the end of a when condition. ([@rrosenblum][]) ### Changes @@ -4100,7 +4363,7 @@ * [#3135](https://github.com/rubocop/rubocop/pull/3135): Add new `Rails/OutputSafety` cop. ([@josh][]) * [#3164](https://github.com/rubocop/rubocop/pull/3164): Add [Fastlane](https://fastlane.tools/)'s Fastfile to the default Includes. ([@jules2689][]) * [#3173](https://github.com/rubocop/rubocop/pull/3173): Make `Style/ModuleFunction` configurable with `module_function` and `extend_self` styles. ([@tjwp][]) -* [#3105](https://github.com/rubocop/rubocop/issues/3105): Add new `Rails/RequestReferer` cop. ([@giannileggio][]) +* [#3105](https://github.com/rubocop/rubocop/issues/3105): Add new `Rails/RequestReferrer` cop. ([@giannileggio][]) * [#3200](https://github.com/rubocop/rubocop/pull/3200): Add auto-correct for `Style/EachForSimpleLoop` cop. ([@tejasbubane][]) * [#3058](https://github.com/rubocop/rubocop/issues/3058): Add new `Style/SpaceInsideArrayPercentLiteral` cop. ([@owst][]) * [#3058](https://github.com/rubocop/rubocop/issues/3058): Add new `Style/SpaceInsidePercentLiteralDelimiters` cop. ([@owst][]) @@ -4675,13 +4938,13 @@ ### New features -* [#2143](https://github.com/rubocop/rubocop/pull/2143): New cop `Performance/CaseWhenSplat` will identify and rearange `case` `when` statements that contain a `when` condition with a splat. ([@rrosenblum][]) +* [#2143](https://github.com/rubocop/rubocop/pull/2143): New cop `Performance/CaseWhenSplat` will identify and rearrange `case` `when` statements that contain a `when` condition with a splat. ([@rrosenblum][]) * New cop `Lint/DuplicatedKey` checks for duplicated keys in hashes, which Ruby 2.2 warns against. ([@sliuu][]) * [#2106](https://github.com/rubocop/rubocop/issues/2106): Add `SuspiciousParamNames` option to `Style/OptionHash`. ([@wli][]) * [#2193](https://github.com/rubocop/rubocop/pull/2193): `Style/Next` supports more `Enumerable` methods. ([@rrosenblum][]) * [#2179](https://github.com/rubocop/rubocop/issues/2179): Add `--list-target-files` option to CLI, which prints the files which will be inspected. ([@maxjacobson][]) * New cop `Style/MutableConstant` checks for assignment of mutable objects to constants. ([@bbatsov][]) -* New cop `Style/RedudantFreeze` checks for usages of `Object#freeze` on immutable objects. ([@bbatsov][]) +* New cop `Style/RedundantFreeze` checks for usages of `Object#freeze` on immutable objects. ([@bbatsov][]) * [#1924](https://github.com/rubocop/rubocop/issues/1924): New option `--cache` and configuration parameter `AllCops: UseCache` turn result caching on (default) or off. ([@jonas054][]) * [#2204](https://github.com/rubocop/rubocop/pull/2204): New cop `Style/StringMethods` will check for preferred method `to_sym` over `intern`. ([@imtayadeway][]) @@ -4849,7 +5112,7 @@ * New cop `Performance/Count` to convert `Enumerable#select...size`, `Enumerable#reject...size`, `Enumerable#select...count`, `Enumerable#reject...count` `Enumerable#select...length`, and `Enumerable#reject...length` to `Enumerable#count`. ([@rrosenblum][]) * `CommentAnnotation` cop does auto-correction. ([@dylandavidson][]) * New cop `Style/TrailingUnderscoreVariable` to remove trailing underscore variables from mass assignment. ([@rrosenblum][]) -* [#1136](https://github.com/rubocop/rubocop/issues/1136): New cop `Performance/ParallelAssignment` to avoid usages of unnessary parallel assignment. ([@rrosenblum][]) +* [#1136](https://github.com/rubocop/rubocop/issues/1136): New cop `Performance/ParallelAssignment` to avoid usages of unnecessary parallel assignment. ([@rrosenblum][]) * [#1278](https://github.com/rubocop/rubocop/issues/1278): `DefEndAlignment` and `EndAlignment` cops do auto-correction. ([@lumeet][]) * `IndentationWidth` cop follows the `AlignWith` option of the `DefEndAlignment` cop. ([@lumeet][]) * [#1837](https://github.com/rubocop/rubocop/issues/1837): New cop `EachWithObjectArgument` checks that `each_with_object` isn't called with an immutable object as argument. ([@jonas054][]) @@ -5734,7 +5997,7 @@ * New cop `AlignArray` keeps track of bad alignment in multi-line array literals. * New cop `SpaceBeforeModifierKeyword` keeps track of missing space before a modifier keyword (`if`, `unless`, `while`, `until`). * New cop `FinalNewline` keeps tracks of the required final newline in a source file. -* Highlightling corrected in `SpaceInsideHashLiteralBraces` and `SpaceAroundBraces` cops. +* Highlighting corrected in `SpaceInsideHashLiteralBraces` and `SpaceAroundBraces` cops. ### Changes @@ -5935,7 +6198,7 @@ * Deprecated `-e`/`--emacs` option. (Use `--format emacs` instead). * Made `progress` formatter the default. * Most formatters (`progress`, `simple` and `clang`) now print relative file paths if the paths are under the current working directory. -* Migrate all cops to new namespaces. `Rubocop::Cop::Lint` is for cops that emit warnings. `Rubocop::Cop::Style` is for cops that do not belong in other namespaces. +* Migrate all cops to new namespaces. `RuboCop::Cop::Lint` is for cops that emit warnings. `RuboCop::Cop::Style` is for cops that do not belong in other namespaces. * Merge `FavorPercentR` and `PercentR` into one cop called `RegexpLiteral`, and add configuration parameter `MaxSlashes`. * Add `CountKeywordArgs` configuration option to `ParameterLists` cop. @@ -6742,3 +7005,14 @@ [@arika]: https://github.com/arika [@soroktree]: https://github.com/soroktree [@alexevanczuk]: https://github.com/alexevanczuk +[@such]: https://github.com/such +[@krishanbhasin-shopify]: https://github.com/krishanbhasin-shopify +[@f1sherman]: https://github.com/f1sherman +[@jaynetics]: https://github.com/jaynetics +[@SparLaimor]: https://github.com/SparLaimor +[@bfad]: https://github.com/bfad +[@istvanfazakas]: https://github.com/istvanfazakas +[@KessaPassa]: https://github.com/KessaPassa +[@jasondoc3]: https://github.com/jasondoc3 +[@ThHareau]: https://github.com/ThHareau +[@ktopolski]: https://github.com/ktopolski diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f3d4901928ef..41e23967e218 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,7 +17,7 @@ do so. ```console $ rubocop -V -1.42.0 (using Parser 2.7.2.0, rubocop-ast 1.1.1, running on ruby 2.7.2) [x86_64-linux] +1.49.0 (using Parser 2.7.2.0, rubocop-ast 1.1.1, running on ruby 2.7.2) [x86_64-linux] - rubocop-performance 1.9.1 - rubocop-rspec 2.0.0 ``` diff --git a/Gemfile b/Gemfile index 48cfdd4176f9..d701e21f8d6b 100644 --- a/Gemfile +++ b/Gemfile @@ -6,12 +6,13 @@ gemspec gem 'asciidoctor' gem 'bump', require: false +gem 'bundler', '>= 1.15.0', '< 3.0' gem 'memory_profiler', platform: :mri gem 'rake', '~> 13.0' gem 'rspec', '~> 3.7' -gem 'rubocop-performance', '~> 1.15.0' +gem 'rubocop-performance', '~> 1.16.0' gem 'rubocop-rake', '~> 0.6.0' -gem 'rubocop-rspec', '~> 2.16.0' +gem 'rubocop-rspec', '~> 2.19.0' # Workaround for cc-test-reporter with SimpleCov 0.18. # Stop upgrading SimpleCov until the following issue will be resolved. # https://github.com/codeclimate/test-reporter/issues/418 diff --git a/README.md b/README.md index ecd4820ed9e2..ac0b05bd904a 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ To prevent an unwanted RuboCop update you might want to use a conservative versi in your `Gemfile`: ```rb -gem 'rubocop', '~> 1.42', require: false +gem 'rubocop', '~> 1.49', require: false ``` See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) for further details. diff --git a/bin/rubocop-profile b/bin/rubocop-profile deleted file mode 100755 index d7e14abe86fb..000000000000 --- a/bin/rubocop-profile +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -if ARGV.include?('-h') || ARGV.include?('--help') - puts "Usage: same as main `rubocop` command but gathers profiling info" - puts "Additional option: `--memory` to print memory usage" - exit(0) -end -with_mem = ARGV.delete('--memory') -ARGV.unshift '--cache', 'false' unless ARGV.include?('--cache') - -require 'stackprof' -if with_mem - require 'memory_profiler' - MemoryProfiler.start -end -StackProf.start -start = Process.clock_gettime(Process::CLOCK_MONOTONIC) -begin - load "#{__dir__}/../exe/rubocop" -ensure - delta = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start - puts "Finished in #{delta.round(1)} seconds" - StackProf.stop - if with_mem - puts "Building memory report..." - report = MemoryProfiler.stop - end - Dir.mkdir('tmp') unless File.exist?('tmp') - StackProf.results('tmp/stackprof.dump') - report&.pretty_print(to_file: 'tmp/memory_profiler.txt', scale_bytes: true) -end diff --git a/changelog/.gitkeep b/changelog/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/config/default.yml b/config/default.yml index 8f9c00a3a7a5..c1ed1cb95007 100644 --- a/config/default.yml +++ b/config/default.yml @@ -153,6 +153,7 @@ AllCops: rubocop-sequel: [sequel] rubocop-rake: [rake] rubocop-graphql: [graphql] + rubocop-capybara: [capybara] # Enable/Disable checking the methods extended by Active Support. ActiveSupportExtensionsEnabled: false @@ -262,6 +263,21 @@ Gemspec/DeprecatedAttributeAssignment: Include: - '**/*.gemspec' +Gemspec/DevelopmentDependencies: + Description: Checks that development dependencies are specified in Gemfile rather than gemspec. + Enabled: pending + VersionAdded: '1.44' + EnforcedStyle: Gemfile + SupportedStyles: + - Gemfile + - gems.rb + - gemspec + AllowedGems: [] + Include: + - '**/*.gemspec' + - '**/Gemfile' + - '**/gems.rb' + Gemspec/DuplicatedAssignment: Description: 'An attribute assignment method calls should be listed only once in a gemspec.' Enabled: true @@ -391,9 +407,8 @@ Layout/AssignmentIndentation: Checks the indentation of the first line of the right-hand-side of a multi-line assignment. Enabled: true - SafeAutoCorrect: false VersionAdded: '0.49' - VersionChanged: '1.40' + VersionChanged: '1.45' # By default the indentation width from `Layout/IndentationWidth` is used, # but it can be overridden by setting this parameter. IndentationWidth: ~ @@ -964,7 +979,6 @@ Layout/IndentationWidth: # Number of spaces for each indentation level. Width: 2 AllowedPatterns: [] - IgnoredPatterns: [] # deprecated Layout/InitialIndentation: Description: >- @@ -992,10 +1006,8 @@ Layout/LineContinuationLeadingSpace: Use trailing spaces instead of leading spaces in strings broken over multiple lines (by a backslash). Enabled: pending - AutoCorrect: false - SafeAutoCorrect: false VersionAdded: '1.31' - VersionChanged: '1.32' + VersionChanged: '1.45' EnforcedStyle: trailing SupportedStyles: - leading @@ -1047,7 +1059,6 @@ Layout/LineLength: # elements. Strings will be converted to Regexp objects. A line that matches # any regular expression listed in this option will be ignored by LineLength. AllowedPatterns: [] - IgnoredPatterns: [] # deprecated Layout/MultilineArrayBraceLayout: Description: >- @@ -1526,7 +1537,6 @@ Lint/AmbiguousBlockAssociation: VersionChanged: '1.13' AllowedMethods: [] AllowedPatterns: [] - IgnoredMethods: [] # deprecated Lint/AmbiguousOperator: Description: >- @@ -1563,7 +1573,9 @@ Lint/AssignmentInCondition: Description: "Don't use assignment in conditions." StyleGuide: '#safe-assignment-in-condition' Enabled: true + SafeAutoCorrect: false VersionAdded: '0.9' + VersionChanged: '1.45' AllowSafeAssignment: true Lint/BigDecimalNew: @@ -1619,8 +1631,7 @@ Lint/Debugger: Description: 'Check for debugger calls.' Enabled: true VersionAdded: '0.14' - VersionChanged: '1.10' - DebuggerReceivers: [] # deprecated + VersionChanged: '1.46' DebuggerMethods: # Groups are available so that a specific group can be disabled in # a user's configuration, but are otherwise not significant. @@ -1648,6 +1659,7 @@ Lint/Debugger: - Kernel.binding.remote_pry - Kernel.binding.pry_remote - Pry.rescue + - pry Rails: - debugger - Kernel.debugger @@ -2037,7 +2049,6 @@ Lint/NumberConversion: SafeAutoCorrect: false AllowedMethods: [] AllowedPatterns: [] - IgnoredMethods: [] # deprecated IgnoredClasses: - Time - DateTime @@ -2388,7 +2399,6 @@ Lint/UnreachableLoop: # RSpec uses `times` in its message expectations # eg. `exactly(2).times` - !ruby/regexp /(exactly|at_least|at_most)\(\d+\)\.times/ - IgnoredPatterns: [] # deprecated Lint/UnusedBlockArgument: Description: 'Checks for unused block arguments.' @@ -2452,6 +2462,11 @@ Lint/UselessMethodDefinition: VersionChanged: '0.91' Safe: false +Lint/UselessRescue: + Description: 'Checks for useless `rescue`s.' + Enabled: pending + VersionAdded: '1.43' + Lint/UselessRuby2Keywords: Description: 'Finds unnecessary uses of `ruby2_keywords`.' Enabled: pending @@ -2493,7 +2508,6 @@ Metrics/AbcSize: # a Float. AllowedMethods: [] AllowedPatterns: [] - IgnoredMethods: [] # deprecated CountRepeatedAttributes: true Max: 17 @@ -2505,13 +2519,11 @@ Metrics/BlockLength: CountComments: false # count full line comments? Max: 25 CountAsOne: [] - ExcludedMethods: [] # deprecated, retained for backwards compatibility AllowedMethods: # By default, exclude the `#refine` method, as it tends to have larger # associated blocks. - refine AllowedPatterns: [] - IgnoredMethods: [] # deprecated Exclude: - '**/*.gemspec' @@ -2533,6 +2545,12 @@ Metrics/ClassLength: Max: 100 CountAsOne: [] +Metrics/CollectionLiteralLength: + Description: 'Checks for `Array` or `Hash` literals with many entries.' + Enabled: pending + VersionAdded: '1.47' + LengthThreshold: 250 + # Avoid complex methods. Metrics/CyclomaticComplexity: Description: >- @@ -2543,7 +2561,6 @@ Metrics/CyclomaticComplexity: VersionChanged: '0.81' AllowedMethods: [] AllowedPatterns: [] - IgnoredMethods: [] # deprecated Max: 7 Metrics/MethodLength: @@ -2555,10 +2572,8 @@ Metrics/MethodLength: CountComments: false # count full line comments? Max: 10 CountAsOne: [] - ExcludedMethods: [] # deprecated, retained for backwards compatibility AllowedMethods: [] AllowedPatterns: [] - IgnoredMethods: [] # deprecated Metrics/ModuleLength: Description: 'Avoid modules longer than 100 lines of code.' @@ -2588,7 +2603,6 @@ Metrics/PerceivedComplexity: VersionChanged: '0.81' AllowedMethods: [] AllowedPatterns: [] - IgnoredMethods: [] # deprecated Max: 8 ################## Migration ############################# @@ -2759,13 +2773,13 @@ Naming/HeredocDelimiterNaming: Enabled: true VersionAdded: '0.50' ForbiddenDelimiters: - - !ruby/regexp '/(^|\s)(EO[A-Z]{1}|END)(\s|$)/' + - !ruby/regexp '/(^|\s)(EO[A-Z]{1}|END)(\s|$)/i' Naming/InclusiveLanguage: Description: 'Recommend the use of inclusive language instead of problematic terms.' Enabled: false VersionAdded: '1.18' - VersionChanged: '1.21' + VersionChanged: '1.49' CheckIdentifiers: true CheckConstants: true CheckVariables: true @@ -2817,7 +2831,6 @@ Naming/MethodName: # - '\A\s*onSelectionCleared\s*' # AllowedPatterns: [] - IgnoredPatterns: [] # deprecated Naming/MethodParameterName: Description: >- @@ -2918,6 +2931,7 @@ Naming/VariableNumber: - rfc822 # Time#rfc822 - rfc2822 # Time#rfc2822 - rfc3339 # DateTime.rfc3339 + - x86_64 # Allowed by default as an underscore separated CPU architecture name AllowedPatterns: [] #################### Security ############################## @@ -3045,6 +3059,7 @@ Style/ArrayCoercion: Style/ArrayIntersect: Description: 'Use `array1.intersect?(array2)` instead of `(array1 & array2).any?`.' Enabled: 'pending' + Safe: false VersionAdded: '1.40' Style/ArrayJoin: @@ -3187,7 +3202,6 @@ Style/BlockDelimiters: - proc - it AllowedPatterns: [] - IgnoredMethods: [] # deprecated # The AllowBracesOnProceduralOneLiners option is ignored unless the # EnforcedStyle is set to `semantic`. If so: # @@ -3211,7 +3225,7 @@ Style/BlockDelimiters: # collection.each do |element| puts element end AllowBracesOnProceduralOneLiners: false # The BracesRequiredMethods overrides all other configurations except - # IgnoredMethods. It can be used to enforce that all blocks for specific + # AllowedMethods. It can be used to enforce that all blocks for specific # methods use braces. For example, you can use this to enforce Sorbet # signatures use braces even when the rest of your codebase enforces # the `line_count_based` style. @@ -3248,6 +3262,9 @@ Style/CaseLikeIf: Enabled: true Safe: false VersionAdded: '0.88' + VersionChanged: '1.48' + # `MinBranchesCount` defines the number of branches `if` needs to have to trigger this cop. + MinBranchesCount: 3 Style/CharacterLiteral: Description: 'Checks for uses of character literals.' @@ -3260,7 +3277,7 @@ Style/ClassAndModuleChildren: StyleGuide: '#namespace-definition' # Moving from compact to nested children requires knowledge of whether the # outer parent is a module or a class. Moving from nested to compact requires - # verification that the outer parent is defined elsewhere. Rubocop does not + # verification that the outer parent is defined elsewhere. RuboCop does not # have the knowledge to perform either operation safely and thus requires # manual oversight. SafeAutoCorrect: false @@ -3305,7 +3322,6 @@ Style/ClassEqualityComparison: - equal? - eql? AllowedPatterns: [] - IgnoredMethods: [] # deprecated Style/ClassMethods: Description: 'Use self when defining module/class methods.' @@ -3426,6 +3442,11 @@ Style/CommentedKeyword: VersionAdded: '0.51' VersionChanged: '1.19' +Style/ComparableClamp: + Description: 'Enforces the use of `Comparable#clamp` instead of comparison by minimum and maximum.' + Enabled: pending + VersionAdded: '1.44' + Style/ConcatArrayLiterals: Description: 'Enforces the use of `Array#push(item)` instead of `Array#concat([item])` to avoid redundant array literals.' Enabled: pending @@ -3487,6 +3508,12 @@ Style/Copyright: Notice: '^Copyright (\(c\) )?2[0-9]{3} .+' AutocorrectNotice: '' +Style/DataInheritance: + Description: 'Checks for inheritance from Data.define.' + StyleGuide: '#no-extend-data-define' + Enabled: pending + VersionAdded: '1.49' + Style/DateTime: Description: 'Use Time over DateTime.' StyleGuide: '#date-time' @@ -3510,6 +3537,12 @@ Style/Dir: Enabled: true VersionAdded: '0.50' +Style/DirEmpty: + Description: >- + Prefer to use `Dir.empty?('path/to/dir')` when checking if a directory is empty. + Enabled: pending + VersionAdded: '1.48' + Style/DisableCopsWithinSourceCodeDirective: Description: >- Forbids disabling/enabling cops within source code. @@ -3705,6 +3738,14 @@ Style/FetchEnvVar: # Environment variables to be excluded from the inspection. AllowedVars: [] +Style/FileEmpty: + Description: >- + Prefer to use `File.empty?('path/to/file')` when checking if a file is empty. + Enabled: pending + Safe: false + SafeAutoCorrect: false + VersionAdded: '1.48' + Style/FileRead: Description: 'Favor `File.(bin)read` convenience methods.' StyleGuide: '#file-read' @@ -3775,7 +3816,6 @@ Style/FormatStringToken: VersionChanged: '1.0' AllowedMethods: [] AllowedPatterns: [] - IgnoredMethods: [] # deprecated Style/FrozenStringLiteralComment: Description: >- @@ -3853,7 +3893,8 @@ Style/HashEachMethods: Safe: false VersionAdded: '0.80' VersionChanged: '1.16' - AllowedReceivers: [] + AllowedReceivers: + - Thread.current Style/HashExcept: Description: >- @@ -4015,16 +4056,41 @@ Style/InverseMethods: :=~: :!~ :<: :>= :>: :<= - # `ActiveSupport` defines some common inverse methods. They are listed below, - # and not enabled by default. - # :present?: :blank?, - # :include?: :exclude? # `InverseBlocks` are methods that are inverted by inverting the return # of the block that is passed to the method InverseBlocks: :select: :reject :select!: :reject! +Style/InvertibleUnlessCondition: + Description: 'Favor `if` with inverted condition over `unless`.' + Enabled: false + VersionAdded: '1.44' + # `InverseMethods` are methods that can be inverted in a `unless` condition. + # The relationship of inverse methods needs to be defined in both directions. + # Keys and values both need to be defined as symbols. + InverseMethods: + :!=: :== + :>: :<= + :<=: :> + :<: :>= + :>=: :< + :!~: :=~ + :zero?: :nonzero? + :nonzero?: :zero? + :any?: :none? + :none?: :any? + :even?: :odd? + :odd?: :even? + # `ActiveSupport` defines some common inverse methods. They are listed below, + # and not enabled by default. + # :present?: :blank? + # :blank?: :present? + # :include?: :exclude? + # :exclude?: :include? + # :one?: :many? + # :many?: :one? + Style/IpAddresses: Description: "Don't include literal IP addresses in code." Enabled: false @@ -4124,9 +4190,7 @@ Style/MethodCallWithArgsParentheses: VersionChanged: '1.7' IgnoreMacros: true AllowedMethods: [] - IgnoredMethods: [] # deprecated AllowedPatterns: [] - IgnoredPatterns: [] # deprecated IncludedMacros: [] AllowParenthesesInMultilineCall: false AllowParenthesesInChaining: false @@ -4143,7 +4207,6 @@ Style/MethodCallWithoutArgsParentheses: Enabled: true AllowedMethods: [] AllowedPatterns: [] - IgnoredMethods: [] # deprecated VersionAdded: '0.47' VersionChanged: '0.55' @@ -4522,7 +4585,6 @@ Style/NumericPredicate: - comparison AllowedMethods: [] AllowedPatterns: [] - IgnoredMethods: [] # deprecated # Exclude RSpec specs because assertions like `expect(1).to be > 0` cause # false positives. Exclude: @@ -4802,6 +4864,11 @@ Style/RedundantFreeze: VersionAdded: '0.34' VersionChanged: '0.66' +Style/RedundantHeredocDelimiterQuotes: + Description: 'Checks for redundant heredoc delimiter quotes.' + Enabled: pending + VersionAdded: '1.45' + Style/RedundantInitialize: Description: 'Checks for redundant `initialize` methods.' Enabled: pending @@ -4817,6 +4884,11 @@ Style/RedundantInterpolation: VersionAdded: '0.76' VersionChanged: '1.30' +Style/RedundantLineContinuation: + Description: 'Check for redundant line continuation.' + Enabled: pending + VersionAdded: '1.49' + Style/RedundantParentheses: Description: "Checks for parentheses that seem not to serve any purpose." Enabled: true @@ -5199,7 +5271,6 @@ Style/SymbolProc: AllowedMethods: - define_method AllowedPatterns: [] - IgnoredMethods: [] # deprecated AllowComments: false Style/TernaryParentheses: @@ -5440,9 +5511,10 @@ Style/YodaCondition: Style/YodaExpression: Description: 'Forbid the use of yoda expressions.' - Enabled: pending + Enabled: false Safe: false VersionAdded: '1.42' + VersionChanged: '1.43' SupportedOperators: - '*' - '+' diff --git a/docs/antora.yml b/docs/antora.yml index f6534f8f038b..b6eea1607fb7 100644 --- a/docs/antora.yml +++ b/docs/antora.yml @@ -2,6 +2,6 @@ name: rubocop title: RuboCop # We always provide version without patch here (e.g. 1.1), # as patch versions should not appear in the docs. -version: '1.42' +version: '1.49' nav: - modules/ROOT/nav.adoc diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 42d33096ab3d..9d4ef7fcfe05 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -6,6 +6,7 @@ ** xref:usage/autocorrect.adoc[Autocorrect] ** xref:usage/caching.adoc[Caching] ** xref:usage/server.adoc[Server] +** xref:usage/profiling.adoc[Profiling] * xref:configuration.adoc[Configuration] * xref:cops.adoc[Cops] * xref:formatters.adoc[Formatters] diff --git a/docs/modules/ROOT/pages/automated_code_review.adoc b/docs/modules/ROOT/pages/automated_code_review.adoc index 032f573c1c96..b697aac983f0 100644 --- a/docs/modules/ROOT/pages/automated_code_review.adoc +++ b/docs/modules/ROOT/pages/automated_code_review.adoc @@ -33,8 +33,3 @@ https://github.com/prontolabs/pronto[Pronto] does quick automated code review of == ReviewDog https://github.com/reviewdog/reviewdog[ReviewDog] is similar to Pronto but with better support for GitHub Actions. - -== Sider - -https://sider.review[Sider] improves your team's productivity by automating code analysis. -It supports RuboCop's autocorrection. diff --git a/docs/modules/ROOT/pages/compatibility.adoc b/docs/modules/ROOT/pages/compatibility.adoc index 5bfc4f613ceb..f4e433299360 100644 --- a/docs/modules/ROOT/pages/compatibility.adoc +++ b/docs/modules/ROOT/pages/compatibility.adoc @@ -32,12 +32,13 @@ The following table is the runtime support matrix. | 2.7 | - | 3.0 | - | 3.1 | - -| 3.2 (experimental) | - +| 3.2 | - +| 3.3 (experimental) | - |=== RuboCop targets Ruby 2.0+ code analysis since RuboCop 1.30. It restored code analysis support that had been removed earlier by mistake, together with dropping runtime support for unsupported Ruby versions. -NOTE: The compatibility xref:configuration.adoc#setting-the-target-ruby-version[target Ruby version mentioned here] is about code analysis (what RuboCop can analyze), not runtime (is RuboCop capable of running on some Ruby or not). +NOTE: The compatibility xref:configuration.adoc#setting-the-target-ruby-version[setting `TargetRubyVersion`] is about code analysis (what RuboCop can analyze), not runtime (is RuboCop capable of running on some Ruby or not). == Forward Compatibility diff --git a/docs/modules/ROOT/pages/configuration.adoc b/docs/modules/ROOT/pages/configuration.adoc index 6b0da94a82e2..a6598f327ced 100644 --- a/docs/modules/ROOT/pages/configuration.adoc +++ b/docs/modules/ROOT/pages/configuration.adoc @@ -483,8 +483,8 @@ are disabled by default. The cop enabling process can be altered by setting `DisabledByDefault` or `EnabledByDefault` (but not both) to `true`. These settings override the default for *all* -cops to disabled or enabled, regardless of the cops' default values (whether enabled, -disabled or pending). +cops to disabled or enabled, except `Lint/Syntax` which is always enabled, +regardless of the cops' default values (whether enabled, disabled or pending). [source,yaml] ---- @@ -492,7 +492,7 @@ AllCops: DisabledByDefault: true ---- -All cops are then disabled by default. Only cops appearing in user +All cops except `Lint/Syntax` are then disabled by default. Only cops appearing in user configuration files with `Enabled: true` will be enabled; every other cop will be disabled without having to explicitly disable them in configuration. It is also possible to enable entire departments by adding for example diff --git a/docs/modules/ROOT/pages/cops.adoc b/docs/modules/ROOT/pages/cops.adoc index afed750c98d7..7ddd73a03c70 100644 --- a/docs/modules/ROOT/pages/cops.adoc +++ b/docs/modules/ROOT/pages/cops.adoc @@ -87,6 +87,7 @@ In the following section you find all available cops: * xref:cops_gemspec.adoc#gemspecdependencyversion[Gemspec/DependencyVersion] * xref:cops_gemspec.adoc#gemspecdeprecatedattributeassignment[Gemspec/DeprecatedAttributeAssignment] +* xref:cops_gemspec.adoc#gemspecdevelopmentdependencies[Gemspec/DevelopmentDependencies] * xref:cops_gemspec.adoc#gemspecduplicatedassignment[Gemspec/DuplicatedAssignment] * xref:cops_gemspec.adoc#gemspecordereddependencies[Gemspec/OrderedDependencies] * xref:cops_gemspec.adoc#gemspecrequiremfa[Gemspec/RequireMFA] @@ -324,6 +325,7 @@ In the following section you find all available cops: * xref:cops_lint.adoc#lintuselessassignment[Lint/UselessAssignment] * xref:cops_lint.adoc#lintuselesselsewithoutrescue[Lint/UselessElseWithoutRescue] * xref:cops_lint.adoc#lintuselessmethoddefinition[Lint/UselessMethodDefinition] +* xref:cops_lint.adoc#lintuselessrescue[Lint/UselessRescue] * xref:cops_lint.adoc#lintuselessruby2keywords[Lint/UselessRuby2Keywords] * xref:cops_lint.adoc#lintuselesssettercall[Lint/UselessSetterCall] * xref:cops_lint.adoc#lintuselesstimes[Lint/UselessTimes] @@ -335,6 +337,7 @@ In the following section you find all available cops: * xref:cops_metrics.adoc#metricsblocklength[Metrics/BlockLength] * xref:cops_metrics.adoc#metricsblocknesting[Metrics/BlockNesting] * xref:cops_metrics.adoc#metricsclasslength[Metrics/ClassLength] +* xref:cops_metrics.adoc#metricscollectionliterallength[Metrics/CollectionLiteralLength] * xref:cops_metrics.adoc#metricscyclomaticcomplexity[Metrics/CyclomaticComplexity] * xref:cops_metrics.adoc#metricsmethodlength[Metrics/MethodLength] * xref:cops_metrics.adoc#metricsmodulelength[Metrics/ModuleLength] @@ -411,13 +414,16 @@ In the following section you find all available cops: * xref:cops_style.adoc#stylecommandliteral[Style/CommandLiteral] * xref:cops_style.adoc#stylecommentannotation[Style/CommentAnnotation] * xref:cops_style.adoc#stylecommentedkeyword[Style/CommentedKeyword] +* xref:cops_style.adoc#stylecomparableclamp[Style/ComparableClamp] * xref:cops_style.adoc#styleconcatarrayliterals[Style/ConcatArrayLiterals] * xref:cops_style.adoc#styleconditionalassignment[Style/ConditionalAssignment] * xref:cops_style.adoc#styleconstantvisibility[Style/ConstantVisibility] * xref:cops_style.adoc#stylecopyright[Style/Copyright] +* xref:cops_style.adoc#styledatainheritance[Style/DataInheritance] * xref:cops_style.adoc#styledatetime[Style/DateTime] * xref:cops_style.adoc#styledefwithparentheses[Style/DefWithParentheses] * xref:cops_style.adoc#styledir[Style/Dir] +* xref:cops_style.adoc#styledirempty[Style/DirEmpty] * xref:cops_style.adoc#styledisablecopswithinsourcecodedirective[Style/DisableCopsWithinSourceCodeDirective] * xref:cops_style.adoc#styledocumentdynamicevaldefinition[Style/DocumentDynamicEvalDefinition] * xref:cops_style.adoc#styledocumentation[Style/Documentation] @@ -443,6 +449,7 @@ In the following section you find all available cops: * xref:cops_style.adoc#styleexplicitblockargument[Style/ExplicitBlockArgument] * xref:cops_style.adoc#styleexponentialnotation[Style/ExponentialNotation] * xref:cops_style.adoc#stylefetchenvvar[Style/FetchEnvVar] +* xref:cops_style.adoc#stylefileempty[Style/FileEmpty] * xref:cops_style.adoc#stylefileread[Style/FileRead] * xref:cops_style.adoc#stylefilewrite[Style/FileWrite] * xref:cops_style.adoc#stylefloatdivision[Style/FloatDivision] @@ -472,6 +479,7 @@ In the following section you find all available cops: * xref:cops_style.adoc#styleinfiniteloop[Style/InfiniteLoop] * xref:cops_style.adoc#styleinlinecomment[Style/InlineComment] * xref:cops_style.adoc#styleinversemethods[Style/InverseMethods] +* xref:cops_style.adoc#styleinvertibleunlesscondition[Style/InvertibleUnlessCondition] * xref:cops_style.adoc#styleipaddresses[Style/IpAddresses] * xref:cops_style.adoc#stylekeywordparametersorder[Style/KeywordParametersOrder] * xref:cops_style.adoc#stylelambda[Style/Lambda] @@ -551,8 +559,10 @@ In the following section you find all available cops: * xref:cops_style.adoc#styleredundantfetchblock[Style/RedundantFetchBlock] * xref:cops_style.adoc#styleredundantfileextensioninrequire[Style/RedundantFileExtensionInRequire] * xref:cops_style.adoc#styleredundantfreeze[Style/RedundantFreeze] +* xref:cops_style.adoc#styleredundantheredocdelimiterquotes[Style/RedundantHeredocDelimiterQuotes] * xref:cops_style.adoc#styleredundantinitialize[Style/RedundantInitialize] * xref:cops_style.adoc#styleredundantinterpolation[Style/RedundantInterpolation] +* xref:cops_style.adoc#styleredundantlinecontinuation[Style/RedundantLineContinuation] * xref:cops_style.adoc#styleredundantparentheses[Style/RedundantParentheses] * xref:cops_style.adoc#styleredundantpercentq[Style/RedundantPercentQ] * xref:cops_style.adoc#styleredundantregexpcharacterclass[Style/RedundantRegexpCharacterClass] diff --git a/docs/modules/ROOT/pages/cops_gemspec.adoc b/docs/modules/ROOT/pages/cops_gemspec.adoc index 16cf6ac10991..84ca12398048 100644 --- a/docs/modules/ROOT/pages/cops_gemspec.adoc +++ b/docs/modules/ROOT/pages/cops_gemspec.adoc @@ -136,6 +136,114 @@ end | Array |=== +== Gemspec/DevelopmentDependencies + +|=== +| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed + +| Pending +| Yes +| No +| 1.44 +| - +|=== + +Enforce that development dependencies for a gem are specified in +`Gemfile`, rather than in the `gemspec` using +`add_development_dependency`. Alternatively, using `EnforcedStyle: +gemspec`, enforce that all dependencies are specified in `gemspec`, +rather than in `Gemfile`. + +=== Examples + +==== EnforcedStyle: Gemfile (default) + +[source,ruby] +---- +# Specify runtime dependencies in your gemspec, +# but all other dependencies in your Gemfile. + +# bad +# example.gemspec +s.add_development_dependency "foo" + +# good +# Gemfile +gem "foo" + +# good +# gems.rb +gem "foo" + +# good (with AllowedGems: ["bar"]) +# example.gemspec +s.add_development_dependency "bar" +---- + +==== EnforcedStyle: gems.rb + +[source,ruby] +---- +# Specify runtime dependencies in your gemspec, +# but all other dependencies in your Gemfile. +# +# Identical to `EnforcedStyle: Gemfile`, but with a different error message. +# Rely on Bundler/GemFilename to enforce the use of `Gemfile` vs `gems.rb`. + +# bad +# example.gemspec +s.add_development_dependency "foo" + +# good +# Gemfile +gem "foo" + +# good +# gems.rb +gem "foo" + +# good (with AllowedGems: ["bar"]) +# example.gemspec +s.add_development_dependency "bar" +---- + +==== EnforcedStyle: gemspec + +[source,ruby] +---- +# Specify all dependencies in your gemspec. + +# bad +# Gemfile +gem "foo" + +# good +# example.gemspec +s.add_development_dependency "foo" + +# good (with AllowedGems: ["bar"]) +# Gemfile +gem "bar" +---- + +=== Configurable attributes + +|=== +| Name | Default value | Configurable values + +| EnforcedStyle +| `Gemfile` +| `Gemfile`, `gems.rb`, `gemspec` + +| AllowedGems +| `[]` +| Array + +| Include +| `+**/*.gemspec+`, `+**/Gemfile+`, `+**/gems.rb+` +| Array +|=== + == Gemspec/DuplicatedAssignment |=== diff --git a/docs/modules/ROOT/pages/cops_layout.adoc b/docs/modules/ROOT/pages/cops_layout.adoc index 79a54b656682..53b3485107c6 100644 --- a/docs/modules/ROOT/pages/cops_layout.adoc +++ b/docs/modules/ROOT/pages/cops_layout.adoc @@ -229,9 +229,9 @@ array = [1, 2, 3, | Enabled | Yes -| Yes (Unsafe) +| Yes | 0.49 -| 1.40 +| 1.45 |=== Checks the indentation of the first line of the @@ -4066,10 +4066,6 @@ end | AllowedPatterns | `[]` | Array - -| IgnoredPatterns -| `[]` -| Array |=== === References @@ -4244,9 +4240,9 @@ end | Pending | Yes -| Yes (Unsafe) +| Yes | 1.31 -| 1.32 +| 1.45 |=== Checks that strings broken over multiple lines (by a backslash) contain @@ -4302,10 +4298,6 @@ instead of trailing spaces. |=== | Name | Default value | Configurable values -| AutoCorrect -| `false` -| Boolean - | EnforcedStyle | `trailing` | `leading`, `trailing` @@ -4577,10 +4569,6 @@ bar: "0000000000", baz: "0000000000"} | AllowedPatterns | `[]` | Array - -| IgnoredPatterns -| `[]` -| Array |=== === References diff --git a/docs/modules/ROOT/pages/cops_lint.adoc b/docs/modules/ROOT/pages/cops_lint.adoc index acaf52aa960e..77bdf413eecc 100644 --- a/docs/modules/ROOT/pages/cops_lint.adoc +++ b/docs/modules/ROOT/pages/cops_lint.adoc @@ -119,10 +119,6 @@ expect { do_something }.to not_change { object.attribute } | AllowedPatterns | `[]` | Array - -| IgnoredMethods -| `[]` -| Array |=== === References @@ -339,9 +335,9 @@ do_something(/pattern/i) | Enabled | Yes -| Yes +| Yes (Unsafe) | 0.9 -| - +| 1.45 |=== Checks for assignments in the conditions of @@ -827,7 +823,7 @@ Login | Yes | No | 0.14 -| 1.10 +| 1.46 |=== Checks for debug calls (such as `debugger` or `binding.pry`) that should @@ -905,12 +901,8 @@ end |=== | Name | Default value | Configurable values -| DebuggerReceivers -| `[]` -| Array - | DebuggerMethods -| `{"Kernel"=>["binding.irb", "Kernel.binding.irb"], "Byebug"=>["byebug", "remote_byebug", "Kernel.byebug", "Kernel.remote_byebug"], "Capybara"=>["save_and_open_page", "save_and_open_screenshot"], "debug.rb"=>["binding.b", "binding.break", "Kernel.binding.b", "Kernel.binding.break"], "Pry"=>["binding.pry", "binding.remote_pry", "binding.pry_remote", "Kernel.binding.pry", "Kernel.binding.remote_pry", "Kernel.binding.pry_remote", "Pry.rescue"], "Rails"=>["debugger", "Kernel.debugger"], "RubyJard"=>["jard"], "WebConsole"=>["binding.console"]}` +| `{"Kernel"=>["binding.irb", "Kernel.binding.irb"], "Byebug"=>["byebug", "remote_byebug", "Kernel.byebug", "Kernel.remote_byebug"], "Capybara"=>["save_and_open_page", "save_and_open_screenshot"], "debug.rb"=>["binding.b", "binding.break", "Kernel.binding.b", "Kernel.binding.break"], "Pry"=>["binding.pry", "binding.remote_pry", "binding.pry_remote", "Kernel.binding.pry", "Kernel.binding.remote_pry", "Kernel.binding.pry_remote", "Pry.rescue", "pry"], "Rails"=>["debugger", "Kernel.debugger"], "RubyJard"=>["jard"], "WebConsole"=>["binding.console"]}` | |=== @@ -936,6 +928,8 @@ Checks for uses of the deprecated class method usages. File.exists?(some_path) Dir.exists?(some_path) iterator? +attr :name, true +attr :name, false ENV.freeze # Calling `Env.freeze` raises `TypeError` since Ruby 2.7. ENV.clone ENV.dup # Calling `Env.dup` raises `TypeError` since Ruby 3.1. @@ -946,6 +940,8 @@ Socket.gethostbyaddr(host) File.exist?(some_path) Dir.exist?(some_path) block_given? +attr_accessor :name +attr_reader :name ENV # `ENV.freeze` cannot prohibit changes to environment variables. ENV.to_h ENV.to_h # `ENV.dup` cannot dup `ENV`, use `ENV.to_h` to get a copy of `ENV` as a hash. @@ -2627,27 +2623,25 @@ the receiver of the call is a HEREDOC. [source,ruby] ---- # bad +<<-SQL + bar +SQL +.strip_indent - <<-SQL - bar - SQL - .strip_indent - - <<-SQL - bar - SQL - .strip_indent - .trim +<<-SQL + bar +SQL +.strip_indent +.trim # good +<<~SQL + bar +SQL - <<~SQL - bar - SQL - - <<~SQL.trim - bar - SQL +<<~SQL.trim + bar +SQL ---- === References @@ -3213,6 +3207,21 @@ class Employee < Person end end +# bad +Employee = Class.new(Person) do + def initialize(name, salary) + @salary = salary + end +end + +# good +Employee = Class.new(Person) do + def initialize(name, salary) + super(name) + @salary = salary + end +end + # bad class Parent def self.inherited(base) @@ -3246,17 +3255,22 @@ because numbered capture is ignored if they're mixed. Replace numbered captures with non-capturing groupings or named captures. - # bad - /(?FOO)(BAR)/ +=== Examples - # good - /(?FOO)(?BAR)/ +[source,ruby] +---- +# bad +/(?FOO)(BAR)/ - # good - /(?FOO)(?:BAR)/ +# good +/(?FOO)(?BAR)/ - # good - /(FOO)(BAR)/ +# good +/(?FOO)(?:BAR)/ + +# good +/(FOO)(BAR)/ +---- == Lint/MultipleComparison @@ -3852,10 +3866,6 @@ Time.now.to_datetime.to_i | `[]` | Array -| IgnoredMethods -| `[]` -| Array - | IgnoredClasses | `Time`, `DateTime` | Array @@ -5795,8 +5805,14 @@ end # good def foo(x, y = 1) + # Alternatives to `__callee__` are `__method__` and `:foo`. return to_enum(__callee__, x, y) - # alternatives to `__callee__` are `__method__` and `:foo` +end + +# good +def foo(x, y = 1) + # It is also allowed if it is wrapped in some method like Sorbet. + return to_enum(T.must(__callee__), x, y) end ---- @@ -6348,10 +6364,6 @@ exactly(2).times { raise StandardError } | AllowedPatterns | `(?-mix:(exactly\|at_least\|at_most)\(\d+\)\.times)` | Array - -| IgnoredPatterns -| `[]` -| Array |=== == Lint/UnusedBlockArgument @@ -6938,6 +6950,65 @@ def method(*args) end ---- +== Lint/UselessRescue + +|=== +| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed + +| Pending +| Yes +| No +| 1.43 +| - +|=== + +Checks for useless `rescue`s, which only reraise rescued exceptions. + +=== Examples + +[source,ruby] +---- +# bad +def foo + do_something +rescue + raise +end + +# bad +def foo + do_something +rescue => e + raise # or 'raise e', or 'raise $!', or 'raise $ERROR_INFO' +end + +# good +def foo + do_something +rescue + do_cleanup + raise +end + +# bad (latest rescue) +def foo + do_something +rescue ArgumentError + # noop +rescue + raise +end + +# good (not the latest rescue) +def foo + do_something +rescue ArgumentError + raise +rescue + # noop +end +---- + == Lint/UselessRuby2Keywords |=== diff --git a/docs/modules/ROOT/pages/cops_metrics.adoc b/docs/modules/ROOT/pages/cops_metrics.adoc index d2dbe7571e68..e8f97e8f2442 100644 --- a/docs/modules/ROOT/pages/cops_metrics.adoc +++ b/docs/modules/ROOT/pages/cops_metrics.adoc @@ -63,10 +63,6 @@ end | `[]` | Array -| IgnoredMethods -| `[]` -| Array - | CountRepeatedAttributes | `true` | Boolean @@ -154,10 +150,6 @@ end # 6 points | `[]` | Array -| ExcludedMethods -| `[]` -| Array - | AllowedMethods | `refine` | Array @@ -166,10 +158,6 @@ end # 6 points | `[]` | Array -| IgnoredMethods -| `[]` -| Array - | Exclude | `+**/*.gemspec+` | Array @@ -282,6 +270,76 @@ end # 6 points | Array |=== +== Metrics/CollectionLiteralLength + +|=== +| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed + +| Pending +| Yes +| No +| 1.47 +| - +|=== + +Checks for literals with extremely many entries. This is indicative of +configuration or data that may be better extracted somewhere else, like +a database, fetched from an API, or read from a non-code file (CSV, +JSON, YAML, etc.). + +=== Examples + +[source,ruby] +---- +# bad +# Huge Array literal +[1, 2, '...', 999_999_999] + +# bad +# Huge Hash literal +{ 1 => 1, 2 => 2, '...' => '...', 999_999_999 => 999_999_999} + +# bad +# Huge Set "literal" +Set[1, 2, '...', 999_999_999] + +# good +# Reasonably sized Array literal +[1, 2, '...', 10] + +# good +# Reading huge Array from external data source +# File.readlines('numbers.txt', chomp: true).map!(&:to_i) + +# good +# Reasonably sized Hash literal +{ 1 => 1, 2 => 2, '...' => '...', 10 => 10} + +# good +# Reading huge Hash from external data source +CSV.foreach('numbers.csv', headers: true).each_with_object({}) do |row, hash| + hash[row["key"].to_i] = row["value"].to_i +end + +# good +# Reasonably sized Set "literal" +Set[1, 2, '...', 10] + +# good +# Reading huge Set from external data source +SomeFramework.config_for(:something)[:numbers].to_set +---- + +=== Configurable attributes + +|=== +| Name | Default value | Configurable values + +| LengthThreshold +| `250` +| Integer +|=== + == Metrics/CyclomaticComplexity |=== @@ -334,10 +392,6 @@ Blocks that are calls to builtin iteration methods | `[]` | Array -| IgnoredMethods -| `[]` -| Array - | Max | `7` | Integer @@ -413,10 +467,6 @@ end # 6 points | `[]` | Array -| ExcludedMethods -| `[]` -| Array - | AllowedMethods | `[]` | Array @@ -424,10 +474,6 @@ end # 6 points | AllowedPatterns | `[]` | Array - -| IgnoredMethods -| `[]` -| Array |=== === References @@ -518,6 +564,20 @@ The maximum number of parameters is configurable. Keyword arguments can optionally be excluded from the total count, as they add less complexity than positional or optional parameters. +Any number of arguments for `initialize` method inside a block of +`Struct.new` and `Data.define` like this is always allowed: + +[source,ruby] +---- +Struct.new(:one, :two, :three, :four, :five, keyword_init: true) do + def initialize(one:, two:, three:, four:, five:) + end +end +---- + +This is because checking the number of arguments of the `initialize` method +does not make sense. + NOTE: Explicit block argument `&block` is not counted to prevent erroneous change that is avoided by making block argument implicit. @@ -662,10 +722,6 @@ end # 7 complexity points | `[]` | Array -| IgnoredMethods -| `[]` -| Array - | Max | `8` | Integer diff --git a/docs/modules/ROOT/pages/cops_naming.adoc b/docs/modules/ROOT/pages/cops_naming.adoc index 8375cfc961ab..358001932ec2 100644 --- a/docs/modules/ROOT/pages/cops_naming.adoc +++ b/docs/modules/ROOT/pages/cops_naming.adoc @@ -591,7 +591,7 @@ EOS | Name | Default value | Configurable values | ForbiddenDelimiters -| `(?-mix:(^\|\s)(EO[A-Z]{1}\|END)(\s\|$))` +| `(?i-mx:(^\|\s)(EO[A-Z]{1}\|END)(\s\|$))` | Array |=== @@ -606,13 +606,14 @@ EOS | Disabled | Yes -| No +| Yes | 1.18 -| 1.21 +| 1.49 |=== Recommends the use of inclusive language instead of problematic terms. The cop can check the following locations for offenses: + - identifiers - constants - variables @@ -620,6 +621,7 @@ The cop can check the following locations for offenses: - symbols - comments - file paths + Each of these locations can be individually enabled/disabled via configuration, for example CheckIdentifiers = true/false. @@ -630,6 +632,9 @@ An AllowedRegex can be specified for a flagged term to exempt allowed uses of th `WholeWord: true` can be set on a flagged term to indicate the cop should only match when a term matches the whole word (partial matches will not be offenses). +The cop supports autocorrection when there is only one suggestion. When there are multiple +suggestions, the best suggestion cannot be identified and will not be autocorrected. + === Examples ==== FlaggedTerms: { whitelist: { Suggestions: ['allowlist'] } } @@ -933,8 +938,8 @@ This cop has `AllowedPatterns` configuration option. Naming/MethodName: AllowedPatterns: - - '\A\s*onSelectionBulkChange\s*' - - '\A\s*onSelectionCleared\s*' + - '\AonSelectionBulkChange\z' + - '\AonSelectionCleared\z' Method names matching patterns are always allowed. @@ -974,10 +979,6 @@ def fooBar; end | AllowedPatterns | `[]` | Array - -| IgnoredPatterns -| `[]` -| Array |=== === References @@ -1487,7 +1488,7 @@ expect(Open3).to receive(:capture3) | Boolean | AllowedIdentifiers -| `capture3`, `iso8601`, `rfc1123_date`, `rfc822`, `rfc2822`, `rfc3339` +| `capture3`, `iso8601`, `rfc1123_date`, `rfc822`, `rfc2822`, `rfc3339`, `x86_64` | Array | AllowedPatterns diff --git a/docs/modules/ROOT/pages/cops_style.adoc b/docs/modules/ROOT/pages/cops_style.adoc index f92a730c7f7a..e6a17920e982 100644 --- a/docs/modules/ROOT/pages/cops_style.adoc +++ b/docs/modules/ROOT/pages/cops_style.adoc @@ -126,8 +126,8 @@ Checks for grouping of accessors in `class` and `module` bodies. By default it enforces accessors to be placed in grouped declarations, but it can be configured to enforce separating them in multiple declarations. -NOTE: `Sorbet` is not compatible with "grouped" style. Consider "separated" style -or disabling this cop. +NOTE: If there is a method call before the accessor method it is always allowed +as it might be intended like Sorbet. === Examples @@ -138,12 +138,25 @@ or disabling this cop. # bad class Foo attr_reader :bar + attr_reader :bax attr_reader :baz end # good class Foo - attr_reader :bar, :baz + attr_reader :bar, :bax, :baz +end + +# good +class Foo + # may be intended comment for bar. + attr_reader :bar + + sig { returns(String) } + attr_reader :bax + + may_be_intended_annotation :baz + attr_reader :baz end ---- @@ -457,8 +470,8 @@ NOTE: Required Ruby version: 3.1 | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed | Pending -| Yes -| Yes +| No +| Yes (Unsafe) | 1.40 | - |=== @@ -473,7 +486,7 @@ The `array1.intersect?(array2)` method is faster than === Safety -This cop cannot guarantee that array1 and array2 are +This cop cannot guarantee that `array1` and `array2` are actually arrays while method `intersect?` is for arrays only. === Examples @@ -1019,10 +1032,6 @@ things.map { |thing| | `[]` | Array -| IgnoredMethods -| `[]` -| Array - | AllowBracesOnProceduralOneLiners | `false` | Boolean @@ -1124,7 +1133,7 @@ self.class === something | No | Yes (Unsafe) | 0.88 -| - +| 1.48 |=== Identifies places where `if-elsif` constructions @@ -1138,6 +1147,8 @@ behavior may be different. === Examples +==== MinBranchesCount: 3 (default) + [source,ruby] ---- # bad @@ -1145,6 +1156,8 @@ if status == :active perform_action elsif status == :inactive || status == :hibernating check_timeout +elsif status == :invalid + report_invalid else final_action end @@ -1155,11 +1168,39 @@ when :active perform_action when :inactive, :hibernating check_timeout +when :invalid + report_invalid +else + final_action +end +---- + +==== MinBranchesCount: 4 + +[source,ruby] +---- +# good +if status == :active + perform_action +elsif status == :inactive || status == :hibernating + check_timeout +elsif status == :invalid + report_invalid else final_action end ---- +=== Configurable attributes + +|=== +| Name | Default value | Configurable values + +| MinBranchesCount +| `3` +| Integer +|=== + === References * https://rubystyle.guide#case-vs-if-else @@ -1226,7 +1267,7 @@ Autocorrection is unsafe. Moving from compact to nested children requires knowledge of whether the outer parent is a module or a class. Moving from nested to compact requires -verification that the outer parent is defined elsewhere. Rubocop does not +verification that the outer parent is defined elsewhere. RuboCop does not have the knowledge to perform either operation safely and thus requires manual oversight. @@ -1367,6 +1408,8 @@ var.class == Date var.class.equal?(Date) var.class.eql?(Date) var.class.name == 'Date' +var.class.to_s == 'Date' +var.class.inspect == 'Date' ---- ==== AllowedMethods: [`==`] @@ -1377,6 +1420,8 @@ var.class.name == 'Date' var.instance_of?(Date) var.class == Date var.class.name == 'Date' +var.class.to_s == 'Date' +var.class.inspect == 'Date' # bad var.class.equal?(Date) @@ -1395,6 +1440,8 @@ var.class == Date var.class.equal?(Date) var.class.eql?(Date) var.class.name == 'Date' +var.class.to_s == 'Date' +var.class.inspect == 'Date' ---- ==== AllowedPatterns: ['eq'] @@ -1409,6 +1456,8 @@ var.class.eql?(Date) # bad var.class == Date var.class.name == 'Date' +var.class.to_s == 'Date' +var.class.inspect == 'Date' ---- === Configurable attributes @@ -1423,10 +1472,6 @@ var.class.name == 'Date' | AllowedPatterns | `[]` | Array - -| IgnoredMethods -| `[]` -| Array |=== === References @@ -2157,6 +2202,52 @@ class X # :nodoc: end ---- +== Style/ComparableClamp + +NOTE: Required Ruby version: 2.4 + +|=== +| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed + +| Pending +| Yes +| Yes +| 1.44 +| - +|=== + +Enforces the use of `Comparable#clamp` instead of comparison by minimum and maximum. + +This cop supports autocorrection for `if/elsif/else` bad style only. +Because `ArgumentError` occurs if the minimum and maximum of `clamp` arguments are reversed. +When these are variables, it is not possible to determine which is the minimum and maximum: + +[source,ruby] +---- +[1, [2, 3].max].min # => 1 +1.clamp(3, 1) # => min argument must be smaller than max argument (ArgumentError) +---- + +=== Examples + +[source,ruby] +---- +# bad +[[x, low].max, high].min + +# bad +if x < low + low +elsif high < x + high +else + x +end + +# good +x.clamp(low, high) +---- + == Style/ConcatArrayLiterals |=== @@ -2438,6 +2529,50 @@ an offense is reported. | String |=== +== Style/DataInheritance + +NOTE: Required Ruby version: 3.2 + +|=== +| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed + +| Pending +| Yes +| Yes +| 1.49 +| - +|=== + +Checks for inheritance from `Data.define` to avoid creating the anonymous parent class. + +=== Safety + +Autocorrection is unsafe because it will change the inheritance +tree (e.g. return value of `Module#ancestors`) of the constant. + +=== Examples + +[source,ruby] +---- +# bad +class Person < Data.define(:first_name, :last_name) + def age + 42 + end +end + +# good +Person = Data.define(:first_name, :last_name) do + def age + 42 + end +end +---- + +=== References + +* https://rubystyle.guide#no-extend-data-define + == Style/DateTime |=== @@ -2607,6 +2742,36 @@ path = File.dirname(File.realpath(__FILE__)) path = __dir__ ---- +== Style/DirEmpty + +NOTE: Required Ruby version: 2.4 + +|=== +| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed + +| Pending +| Yes +| Yes +| 1.48 +| - +|=== + +Prefer to use `Dir.empty?('path/to/dir')` when checking if a directory is empty. + +=== Examples + +[source,ruby] +---- +# bad +Dir.entries('path/to/dir').size == 2 +Dir.children('path/to/dir').empty? +Dir.children('path/to/dir').size == 0 +Dir.each_child('path/to/dir').none? + +# good +Dir.empty?('path/to/dir') +---- + == Style/DisableCopsWithinSourceCodeDirective |=== @@ -2869,6 +3034,10 @@ Checks for missing documentation comment for public methods. It can optionally be configured to also require documentation for non-public methods. +NOTE: This cop allows `initialize` method because `initialize` is +a special method called from `new`. In some programming languages +they are called constructor to distinguish it from method. + === Examples [source,ruby] @@ -4146,6 +4315,46 @@ ENV['X'].some_method # (e.g. `.nil?`) * https://rubystyle.guide/#hash-fetch-defaults +== Style/FileEmpty + +NOTE: Required Ruby version: 2.4 + +|=== +| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed + +| Pending +| No +| Yes (Unsafe) +| 1.48 +| - +|=== + +Prefer to use `File.empty?('path/to/file')` when checking if a file is empty. + +=== Safety + +This cop's autocorrection is unsafe it because `File.size`, `File.read`, +and `File.binread` raise `ENOENT` exception when there is no file +corresponding to the path, while `File.empty?` does not raise an exception. + +=== Examples + +[source,ruby] +---- +# bad +File.zero?('path/to/file') +File.size('path/to/file') == 0 +File.size('path/to/file') >= 0 +File.size('path/to/file').zero? +File.read('path/to/file').empty? +File.binread('path/to/file') == '' +FileTest.zero?('path/to/file') + +# good +File.empty?('path/to/file') +FileTest.empty?('path/to/file') +---- + == Style/FileRead |=== @@ -4639,10 +4848,6 @@ redirect('foo/%{bar_id}') | AllowedPatterns | `[]` | Array - -| IgnoredMethods -| `[]` -| Array |=== == Style/FrozenStringLiteralComment @@ -5171,7 +5376,7 @@ execute(sql).values.each { |v| p v } | Name | Default value | Configurable values | AllowedReceivers -| `[]` +| `Thread.current` | Array |=== @@ -5411,6 +5616,9 @@ The supported styles are: # good {foo: foo, bar: bar} +# good +{foo: foo, bar:} + # good {foo:, bar:} ---- @@ -5774,6 +5982,32 @@ The maximum line length is configured in the `Layout/LineLength` cop. The tab size is configured in the `IndentationWidth` of the `Layout/IndentationStyle` cop. +One-line pattern matching is always allowed. To ensure that there are few cases +where the match variable is not used, and to prevent oversights. The variable `x` +becomes undefined and raises `NameError` when the following example is changed to +the modifier form: + +[source,ruby] +---- +if [42] in [x] + x # `x` is undefined when using modifier form. +end +---- + +NOTE: It is allowed when `defined?` argument has an undefined value, +because using the modifier form causes the following incompatibility: + +[source,ruby] +---- +unless defined?(undefined_foo) + undefined_foo = 'default_value' +end +undefined_foo # => 'default_value' + +undefined_bar = 'default_value' unless defined?(undefined_bar) +undefined_bar # => nil +---- + === Examples [source,ruby] @@ -6162,6 +6396,74 @@ end | |=== +== Style/InvertibleUnlessCondition + +|=== +| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed + +| Disabled +| Yes +| Yes +| 1.44 +| - +|=== + +Checks for usages of `unless` which can be replaced by `if` with inverted condition. +Code without `unless` is easier to read, but that is subjective, so this cop +is disabled by default. + +Methods that can be inverted should be defined in `InverseMethods`. Note that +the relationship of inverse methods needs to be defined in both directions. +For example, + InverseMethods: + :!=: :== + :even?: :odd? + :odd?: :even? + + will suggest both `even?` and `odd?` to be inverted, but only `!=` (and not `==`). + +=== Safety + +This cop is unsafe because it cannot be guaranteed that the method +and its inverse method are both defined on receiver, and also are +actually inverse of each other. + +=== Examples + +[source,ruby] +---- +# bad (simple condition) +foo unless !bar +foo unless x != y +foo unless x >= 10 +foo unless x.even? + +# good +foo if bar +foo if x == y +foo if x < 10 +foo if x.odd? + +# bad (complex condition) +foo unless x != y || x.even? + +# good +foo if x == y && x.odd? + +# good (if) +foo if !condition +---- + +=== Configurable attributes + +|=== +| Name | Default value | Configurable values + +| InverseMethods +| `{:!==>:==, :>=>:<=, :<==>:>, :<=>:>=, :>==>:<, :!~=>:=~, :zero?=>:nonzero?, :nonzero?=>:zero?, :any?=>:none?, :none?=>:any?, :even?=>:odd?, :odd?=>:even?}` +| +|=== + == Style/IpAddresses |=== @@ -6734,21 +7036,19 @@ Enforces the presence (default) or absence of parentheses in method calls containing parameters. In the default style (require_parentheses), macro methods are allowed. -Additional methods can be added to the `AllowedMethods` -or `AllowedPatterns` list. These options are -valid only in the default style. Macros can be included by -either setting `IgnoreMacros` to false or adding specific macros to -the `IncludedMacros` list. +Additional methods can be added to the `AllowedMethods` or +`AllowedPatterns` list. These options are valid only in the default +style. Macros can be included by either setting `IgnoreMacros` to false +or adding specific macros to the `IncludedMacros` list. -Precedence of options is all follows: +Precedence of options is as follows: 1. `AllowedMethods` 2. `AllowedPatterns` 3. `IncludedMacros` -eg. If a method is listed in both -`IncludedMacros` and `AllowedMethods`, then the latter takes -precedence (that is, the method is allowed). +If a method is listed in both `IncludedMacros` and `AllowedMethods`, +then the latter takes precedence (that is, the method is allowed). In the alternative style (omit_parentheses), there are three additional options. @@ -6767,14 +7067,29 @@ options. to `true` allows the presence of parentheses in such a method call even with arguments. -NOTE: Parentheses are still allowed in cases where omitting them -results in ambiguous or syntactically incorrect code. For example, -parentheses are required around a method with arguments when inside an -endless method definition introduced in Ruby 3.0. Parentheses are also -allowed when forwarding arguments with the triple-dot syntax introduced -in Ruby 2.7 as omitting them starts an endless range. -And Ruby 3.1's hash omission syntax has a case that requires parentheses -because of the following issue: https://bugs.ruby-lang.org/issues/18396. +NOTE: The style of `omit_parentheses` allows parentheses in cases where +omitting them results in ambiguous or syntactically incorrect code. + +Non-exhaustive list of examples: + +- Parentheses are required allowed in method calls with arguments inside + literals, logical operators, setting default values in position and + keyword arguments, chaining and more. +- Parentheses are allowed in method calls with arguments inside + operators to avoid ambiguity. + triple-dot syntax introduced in Ruby 2.7 as omitting them starts an + endless range. +- Parentheses are allowed when forwarding arguments with the + triple-dot syntax introduced in Ruby 2.7 as omitting them starts an + endless range. +- Parentheses are required in calls with arguments when inside an + endless method definition introduced in Ruby 3.0. +- Ruby 3.1's hash omission syntax allows parentheses if the method call + is in conditionals and requires parentheses if the call + is not the value-returning expression. See + https://bugs.ruby-lang.org/issues/18396. +- Parentheses are required in anonymous arguments, keyword arguments + and block passing in Ruby 3.2. === Examples @@ -6814,34 +7129,28 @@ array.delete(e) array.delete e # bad -foo.enforce(strict: true) +action.enforce(strict: true) # good -foo.enforce strict: true +action.enforce strict: true # good -# Allows parens for calls that won't produce valid Ruby or be ambiguous. -model.validate strict(true) +# Parentheses are allowed for code that can be ambiguous without +# them. +action.enforce(condition) || other_condition # good -# Allows parens for calls that won't produce valid Ruby or be ambiguous. +# Parentheses are allowed for calls that won't produce valid Ruby +# without them. yield path, File.basename(path) # good -# Operators methods calls with parens -array&.[](index) - -# good -# Operators methods without parens, if you prefer -array.[] index - -# good -# Operators methods calls with parens -array&.[](index) - -# good -# Operators methods without parens, if you prefer -array.[] index +# Omitting the parentheses in Ruby 3.1 hash omission syntax can lead +# to ambiguous code. We allow them in conditionals and non-last +# expressions. See https://bugs.ruby-lang.org/issues/18396 +if meets(criteria:, action:) + safe_action(action) || dangerous_action(action) +end ---- ==== IgnoreMacros: true (default) @@ -6971,18 +7280,10 @@ Array 1 | `[]` | Array -| IgnoredMethods -| `[]` -| Array - | AllowedPatterns | `[]` | Array -| IgnoredPatterns -| `[]` -| Array - | IncludedMacros | `[]` | Array @@ -7068,10 +7369,6 @@ object.foo() | AllowedPatterns | `[]` | Array - -| IgnoredMethods -| `[]` -| Array |=== === References @@ -7330,7 +7627,7 @@ a <= b ? a : b | Disabled | Yes -| No +| Yes | 0.30 | 0.38 |=== @@ -9059,10 +9356,11 @@ parameter but this maximum can be configured by setting `Max`. [source,ruby] ---- # bad -foo { _1.call(_2, _3, _4) } +use_multiple_numbered_parameters { _1.call(_2, _3, _4) } # good -foo { do_something(_1) } +array.each { use_array_element_as_numbered_parameter(_1) } +hash.each { use_only_hash_value_as_numbered_parameter(_2) } ---- === Configurable attributes @@ -9357,10 +9655,6 @@ bar.baz.positive? | `[]` | Array -| IgnoredMethods -| `[]` -| Array - | Exclude | `+spec/**/*+` | Array @@ -10930,6 +11224,44 @@ CONST = 1.freeze CONST = 1 ---- +== Style/RedundantHeredocDelimiterQuotes + +|=== +| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed + +| Pending +| Yes +| Yes +| 1.45 +| - +|=== + +Checks for redundant heredoc delimiter quotes. + +=== Examples + +[source,ruby] +---- +# bad +do_something(<<~'EOS') + no string interpolation style text +EOS + +# good +do_something(<<~EOS) + no string interpolation style text +EOS + +do_something(<<~'EOS') + #{string_interpolation_style_text_not_evaluated} +EOS + +do_something(<<~'EOS') + Preserve \ + newlines +EOS +---- + == Style/RedundantInitialize |=== @@ -11104,6 +11436,83 @@ y << 'b' # raise `FrozenError`. @var ---- +== Style/RedundantLineContinuation + +|=== +| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed + +| Pending +| Yes +| Yes +| 1.49 +| - +|=== + +Check for redundant line continuation. + +This cop marks a line continuation as redundant if removing the backslash +does not result in a syntax error. +However, a backslash at the end of a comment or +for string concatenation is not redundant and is not considered an offense. + +=== Examples + +[source,ruby] +---- +# bad +foo. \ + bar +foo \ + &.bar \ + .baz + +# good +foo. + bar +foo + &.bar + .baz + +# bad +[foo, \ + bar] +{foo: \ + bar} + +# good +[foo, + bar] +{foo: + bar} + +# bad +foo(bar, \ + baz) + +# good +foo(bar, + baz) + +# also good - backslash in string concatenation is not redundant +foo('bar' \ + 'baz') + +# also good - backslash at the end of a comment is not redundant +foo(bar, # \ + baz) + +# also good - backslash at the line following the newline begins with a + or -, +# it is not redundant +1 \ + + 2 \ + - 3 + +# also good - backslash with newline between the method name and its arguments, +# it is not redundant. +some_method \ + (argument) +---- + == Style/RedundantParentheses |=== @@ -12718,7 +13127,7 @@ Checks that arrays are sliced with endless ranges instead of === Safety This cop is unsafe because `x..-1` and `x..` are only guaranteed to -be equivalent for `Array#[]`, and the cop cannot determine what class +be equivalent for `Array#[]`, `String#[]`, and the cop cannot determine what class the receiver is. For example: @@ -13737,10 +14146,6 @@ something.map { |s| s.upcase } | `[]` | Array -| IgnoredMethods -| `[]` -| Array - | AllowComments | `false` | Boolean @@ -14768,6 +15173,7 @@ It discourages such code, as the condition becomes more difficult to read and understand. This cop supports two styles: + - `forbid_mixed_logical_operators` (default) - `forbid_logical_operators` @@ -15184,12 +15590,14 @@ obj == 'string' #=> true "bar" != foo 42 >= foo 10 < bar +99 == CONST # good foo == 99 foo == "bar" foo <= 42 bar > 10 +CONST == 99 "#{interpolation}" == foo /#{interpolation}/ == foo ---- @@ -15256,17 +15664,24 @@ bar > 10 |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed -| Pending +| Disabled | No | Yes (Unsafe) | 1.42 -| - +| 1.43 |=== Forbids Yoda expressions, i.e. binary operations (using `*`, `+`, `&`, `|`, and `^` operators) where the order of expression is reversed, eg. `1 + x`. This cop complements `Style/YodaCondition` cop, which has a similar purpose. +This cop is disabled by default to respect user intentions such as: + +[source,ruby] +---- +config.server_port = 9000 + ENV["TEST_ENV_NUMBER"].to_i +---- + === Safety This cop is unsafe because binary operators can be defined @@ -15283,12 +15698,14 @@ have the same result if reversed. 1 + x 10 * y 1 & z +1 + CONST # good 60 * 24 x + 1 y * 10 z & 1 +CONST + 1 # good 1 | x @@ -15315,10 +15732,13 @@ z & 1 |=== Checks for numeric comparisons that can be replaced -by a predicate method, such as receiver.length == 0, -receiver.length > 0, receiver.length != 0, -receiver.length < 1 and receiver.size == 0 that can be -replaced by receiver.empty? and !receiver.empty?. +by a predicate method, such as `receiver.length == 0`, +`receiver.length > 0`, and `receiver.length != 0`, +`receiver.length < 1` and `receiver.size == 0` that can be +replaced by `receiver.empty?` and `!receiver.empty?`. + +NOTE: `File`, `Tempfile`, and `StringIO` do not have `empty?` +so allow `size == 0` and `size.zero?`. === Safety diff --git a/docs/modules/ROOT/pages/extensions.adoc b/docs/modules/ROOT/pages/extensions.adoc index 80ad5794f85e..f2fa57acceb1 100644 --- a/docs/modules/ROOT/pages/extensions.adoc +++ b/docs/modules/ROOT/pages/extensions.adoc @@ -84,6 +84,8 @@ Rake-specific analysis Code style checking for Sequel gem * https://github.com/rubocop/rubocop-thread_safety[rubocop-thread_safety] - Thread-safety analysis +* https://github.com/rubocop/rubocop-capybara[rubocop-capybara] - +Capybara-specific analysis ==== Third-party Extensions @@ -200,3 +202,43 @@ For example, when you have defined `MyCustomFormatter` in ---- $ rubocop --require ./path/to/my_custom_formatter --format MyCustomFormatter ---- + +== Template support + +RuboCop has API for extensions to support templates such as ERB, Haml, Slim, etc. + +Normally, RuboCop extracts one Ruby code from one Ruby file, however there are multiple embedded Ruby codes in one template file. To solve this problem, RuboCop has a mechanism called `RuboCop::Runner.ruby_extractors`, to which any Ruby extractor can be added on the extension side. + +Ruby extractor must be a callable object that takes a `RuboCop::ProcessedSource` and returns an `Array` of `Hash` that contains Ruby source codes and their offsets from original source code, or returns `nil` for unrelated file. + +[source,ruby] +--- +ruby_extractor.call(processed_source) +--- + +An example returned value from a Ruby extractor would be as follows: + +[source] +--- +[ + { + offset: 2, + processed_source: # + }, + { + offset: 10, + processed_source: # + }, +] +--- + +On the extension side, the code would be something like this: + +[source,ruby] +--- +RuboCop::Runner.ruby_extractors.unshift(ruby_extractor) +--- + +`RuboCop::Runners.ruby_extractors` is processed from the beginning and ends when one of them returns a non-nil value. By default, there is a Ruby extractor that returns the given Ruby source code with offset 0, so you can unshift any Ruby extractor before it. + +NOTE: This is still an experimental feature and may change in the future. diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc index a19e3b43139a..0ee23a71f8a1 100644 --- a/docs/modules/ROOT/pages/index.adoc +++ b/docs/modules/ROOT/pages/index.adoc @@ -25,7 +25,7 @@ linter: * A vast number of ready-made xref:extensions.adoc[extensions] (e.g. `rubocop-rails`, `rubocop-rspec`, `rubocop-performance` and `rubocop-minitest`) * Wide editor/IDE support -* Many online services use RuboCop internally (e.g. HoundCI, Sider and CodeClimate) +* Many online services use RuboCop internally (e.g. HoundCI and CodeClimate) * Best logo/stickers ever The project is closely tied to several efforts to document and promote the best practices of the Ruby community: diff --git a/docs/modules/ROOT/pages/installation.adoc b/docs/modules/ROOT/pages/installation.adoc index eacd3d8c3315..582d3eb5cd26 100644 --- a/docs/modules/ROOT/pages/installation.adoc +++ b/docs/modules/ROOT/pages/installation.adoc @@ -22,7 +22,7 @@ in your `Gemfile`: [source,rb] ---- -gem 'rubocop', '~> 1.42', require: false +gem 'rubocop', '~> 1.49', require: false ---- .A Modular RuboCop diff --git a/docs/modules/ROOT/pages/usage/profiling.adoc b/docs/modules/ROOT/pages/usage/profiling.adoc new file mode 100644 index 000000000000..1d28a98c0e59 --- /dev/null +++ b/docs/modules/ROOT/pages/usage/profiling.adoc @@ -0,0 +1,14 @@ += Profiling + +NOTE: Profiling was introduced in RuboCop 1.45. + +RuboCop comes with the ability to profile itself while running. It has 2 CLI options: + +* the `--profile` option will use a https://github.com/tmm1/stackprof[stackprof gem](you need to have it in `Gemfile`) to profile wall time and print a report at the `tmp/rubocop-stackprof.dump` location of the project. See the gem's documentation on how to interpret the results +* the `--memory` option will additionally profile memory usage using https://github.com/SamSaffron/memory_profiler[memory_profiler gem](you need to have it in `Gemfile`) and print a report at the `tmp/rubocop-memory_profiler.txt` location of the project + +NOTE: Profiling memory can be very costly and time consuming when run on a large codebase. In this case, it is recommended to use it over a limited set of files (`app/` directory, for example) or use a limited set of cops (with `--only`, for example). + +== Reporting Back + +If you found a performance problem in RuboCop, you are encouraged to report back via an issue or a pull request. See xref:contributing.adoc[Contributing Guide] for more details. diff --git a/docs/modules/ROOT/pages/usage/server.adoc b/docs/modules/ROOT/pages/usage/server.adoc index 4f4d1c6139f6..28ce6ec0e6c2 100644 --- a/docs/modules/ROOT/pages/usage/server.adoc +++ b/docs/modules/ROOT/pages/usage/server.adoc @@ -57,6 +57,12 @@ RuboCop version incompatibility found, RuboCop server restarting... RuboCop server starting on 127.0.0.1:60665. ``` +If you would like to start the server in the foreground, which can be useful when running within Docker, you can pass the `--no-detach` option. + +```console +% rubocop --start-server --no-detach +``` + == Restart Server The started server does not reload the configuration file. @@ -92,6 +98,9 @@ These are the command-line options for server operations: | `--server-status` | Show server status. + +| `--no-detach` +| Run the server process in the foreground. |=== TIP: You can specify the server host and port with the $RUBOCOP_SERVER_HOST and the $RUBOCOP_SERVER_PORT environment variables. diff --git a/docs/modules/ROOT/pages/v1_upgrade_notes.adoc b/docs/modules/ROOT/pages/v1_upgrade_notes.adoc index 808a9258e696..822b133e036d 100644 --- a/docs/modules/ROOT/pages/v1_upgrade_notes.adoc +++ b/docs/modules/ROOT/pages/v1_upgrade_notes.adoc @@ -156,9 +156,9 @@ _Legacy:_ interface allowed for a `node`, with an optional `location` (symbol or _Current:_ pass a range (or node as a shortcut for `node.loc.expression`), no `location:`. No abuse tolerated. -==== de-dupping changes +==== deduping changes -Both de-dup on `range` and won't process the duplicated offenses at all. +Both dedupe on `range` and won't process the duplicated offenses at all. _Legacy:_ if offenses on same `node` but different `range`: considered as multiple offenses but a single autocorrect call. diff --git a/docs/modules/ROOT/partials/cops_layout_footer.adoc b/docs/modules/ROOT/partials/cops_layout_footer.adoc index f51d1faaa839..a447bf53e8a3 100644 --- a/docs/modules/ROOT/partials/cops_layout_footer.adoc +++ b/docs/modules/ROOT/partials/cops_layout_footer.adoc @@ -24,7 +24,7 @@ that are already short enough, and only considered multiline because of their last element. Each cop can be configured independently allowing for more fine-grained -control ovwer what is considered good ok in the codebase. +control over what is considered good ok in the codebase. === Examples diff --git a/lib/rubocop.rb b/lib/rubocop.rb index 429a78d4bb83..27da07cf1bb0 100644 --- a/lib/rubocop.rb +++ b/lib/rubocop.rb @@ -14,6 +14,7 @@ require 'rubocop-ast' require_relative 'rubocop/ast_aliases' +require_relative 'rubocop/ext/comment' require_relative 'rubocop/ext/range' require_relative 'rubocop/ext/regexp_node' require_relative 'rubocop/ext/regexp_parser' @@ -101,6 +102,7 @@ require_relative 'rubocop/cop/mixin/method_complexity' require_relative 'rubocop/cop/mixin/method_preference' require_relative 'rubocop/cop/mixin/min_body_length' +require_relative 'rubocop/cop/mixin/min_branches_count' require_relative 'rubocop/cop/mixin/multiline_element_indentation' require_relative 'rubocop/cop/mixin/multiline_element_line_breaks' require_relative 'rubocop/cop/mixin/multiline_expression_indentation' @@ -166,6 +168,7 @@ require_relative 'rubocop/cop/gemspec/dependency_version' require_relative 'rubocop/cop/gemspec/deprecated_attribute_assignment' +require_relative 'rubocop/cop/gemspec/development_dependencies' require_relative 'rubocop/cop/gemspec/duplicated_assignment' require_relative 'rubocop/cop/gemspec/ordered_dependencies' require_relative 'rubocop/cop/gemspec/require_mfa' @@ -399,6 +402,7 @@ require_relative 'rubocop/cop/lint/useless_assignment' require_relative 'rubocop/cop/lint/useless_else_without_rescue' require_relative 'rubocop/cop/lint/useless_method_definition' +require_relative 'rubocop/cop/lint/useless_rescue' require_relative 'rubocop/cop/lint/useless_ruby2_keywords' require_relative 'rubocop/cop/lint/useless_setter_call' require_relative 'rubocop/cop/lint/useless_times' @@ -413,6 +417,7 @@ require_relative 'rubocop/cop/metrics/block_length' require_relative 'rubocop/cop/metrics/block_nesting' require_relative 'rubocop/cop/metrics/class_length' +require_relative 'rubocop/cop/metrics/collection_literal_length' require_relative 'rubocop/cop/metrics/method_length' require_relative 'rubocop/cop/metrics/module_length' require_relative 'rubocop/cop/metrics/parameter_lists' @@ -470,13 +475,16 @@ require_relative 'rubocop/cop/style/command_literal' require_relative 'rubocop/cop/style/comment_annotation' require_relative 'rubocop/cop/style/commented_keyword' +require_relative 'rubocop/cop/style/comparable_clamp' require_relative 'rubocop/cop/style/concat_array_literals' require_relative 'rubocop/cop/style/conditional_assignment' require_relative 'rubocop/cop/style/constant_visibility' require_relative 'rubocop/cop/style/copyright' +require_relative 'rubocop/cop/style/data_inheritance' require_relative 'rubocop/cop/style/date_time' require_relative 'rubocop/cop/style/def_with_parentheses' require_relative 'rubocop/cop/style/dir' +require_relative 'rubocop/cop/style/dir_empty' require_relative 'rubocop/cop/style/disable_cops_within_source_code_directive' require_relative 'rubocop/cop/style/documentation_method' require_relative 'rubocop/cop/style/documentation' @@ -502,6 +510,7 @@ require_relative 'rubocop/cop/style/explicit_block_argument' require_relative 'rubocop/cop/style/exponential_notation' require_relative 'rubocop/cop/style/fetch_env_var' +require_relative 'rubocop/cop/style/file_empty' require_relative 'rubocop/cop/style/file_read' require_relative 'rubocop/cop/style/file_write' require_relative 'rubocop/cop/style/float_division' @@ -531,6 +540,7 @@ require_relative 'rubocop/cop/style/infinite_loop' require_relative 'rubocop/cop/style/inverse_methods' require_relative 'rubocop/cop/style/inline_comment' +require_relative 'rubocop/cop/style/invertible_unless_condition' require_relative 'rubocop/cop/style/ip_addresses' require_relative 'rubocop/cop/style/keyword_parameters_order' require_relative 'rubocop/cop/style/lambda' @@ -552,7 +562,9 @@ require_relative 'rubocop/cop/style/redundant_each' require_relative 'rubocop/cop/style/redundant_fetch_block' require_relative 'rubocop/cop/style/redundant_file_extension_in_require' +require_relative 'rubocop/cop/style/redundant_heredoc_delimiter_quotes' require_relative 'rubocop/cop/style/redundant_initialize' +require_relative 'rubocop/cop/style/redundant_line_continuation' require_relative 'rubocop/cop/style/redundant_self_assignment' require_relative 'rubocop/cop/style/redundant_self_assignment_branch' require_relative 'rubocop/cop/style/require_order' diff --git a/lib/rubocop/cli.rb b/lib/rubocop/cli.rb index f3f8970d41c9..2b198cbb7961 100644 --- a/lib/rubocop/cli.rb +++ b/lib/rubocop/cli.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'fileutils' + module RuboCop # The CLI is a class responsible of handling all the command line interface # logic. @@ -38,14 +40,16 @@ def run(args = ARGV) @options, paths = Options.new.parse(args) @env = Environment.new(@options, @config_store, paths) - if @options[:init] - run_command(:init) - else - act_on_options - validate_options_vs_config - parallel_by_default! - apply_default_formatter - execute_runners + profile_if_needed do + if @options[:init] + run_command(:init) + else + act_on_options + validate_options_vs_config + parallel_by_default! + apply_default_formatter + execute_runners + end end rescue ConfigNotFoundError, IncorrectCopNameError, OptionArgumentError => e warn e.message @@ -68,6 +72,48 @@ def run(args = ARGV) private + # rubocop:disable Metrics/MethodLength, Metrics/AbcSize + def profile_if_needed + return yield unless @options[:profile] + + return STATUS_ERROR unless require_gem('stackprof') + + with_memory = @options[:memory] + if with_memory + return STATUS_ERROR unless require_gem('memory_profiler') + + MemoryProfiler.start + end + + tmp_dir = File.join(ConfigFinder.project_root, 'tmp') + FileUtils.mkdir_p(tmp_dir) + cpu_profile_file = File.join(tmp_dir, 'rubocop-stackprof.dump') + status = nil + + StackProf.run(out: cpu_profile_file) do + status = yield + end + puts "Profile report generated at #{cpu_profile_file}" + + if with_memory + puts 'Building memory report...' + report = MemoryProfiler.stop + memory_profile_file = File.join(tmp_dir, 'rubocop-memory_profiler.txt') + report.pretty_print(to_file: memory_profile_file, scale_bytes: true) + puts "Memory report generated at #{memory_profile_file}" + end + status + end + # rubocop:enable Metrics/MethodLength, Metrics/AbcSize + + def require_gem(name) + require name + true + rescue LoadError + warn("You don't have #{name} installed. Add it to your Gemfile and run `bundle install`") + false + end + def run_command(name) @env.run(name) end diff --git a/lib/rubocop/cli/command/auto_generate_config.rb b/lib/rubocop/cli/command/auto_generate_config.rb index 420ca7bd99e2..8b5954bea64a 100644 --- a/lib/rubocop/cli/command/auto_generate_config.rb +++ b/lib/rubocop/cli/command/auto_generate_config.rb @@ -16,6 +16,7 @@ class AutoGenerateConfig < Base PHASE_1_OVERRIDDEN = '(skipped because the default Layout/LineLength:Max is overridden)' PHASE_1_DISABLED = '(skipped because Layout/LineLength is disabled)' + PHASE_1_SKIPPED = '(skipped because a list of cops is passed to the `--only` flag)' def run add_formatter @@ -31,6 +32,8 @@ def maybe_run_line_length_cop skip_line_length_cop(PHASE_1_DISABLED) elsif !same_max_line_length?(@config_store.for_pwd, ConfigLoader.default_configuration) skip_line_length_cop(PHASE_1_OVERRIDDEN) + elsif options_has_only_flag? + skip_line_length_cop(PHASE_1_SKIPPED) else run_line_length_cop end @@ -57,6 +60,10 @@ def line_length_cop(config) config.for_cop('Layout/LineLength') end + def options_has_only_flag? + @options[:only] + end + # Do an initial run with only Layout/LineLength so that cops that # depend on Layout/LineLength:Max get the correct value for that # parameter. diff --git a/lib/rubocop/cli/command/execute_runner.rb b/lib/rubocop/cli/command/execute_runner.rb index c5360f62c5e7..a1eaac1f5461 100644 --- a/lib/rubocop/cli/command/execute_runner.rb +++ b/lib/rubocop/cli/command/execute_runner.rb @@ -76,13 +76,18 @@ def display_error_summary(errors) warn <<~WARNING Errors are usually caused by RuboCop bugs. Please, report your problems to RuboCop's issue tracker. - #{Gem.loaded_specs['rubocop'].metadata['bug_tracker_uri']} - + #{bug_tracker_uri} Mention the following information in the issue report: #{RuboCop::Version.version(debug: true)} WARNING end + def bug_tracker_uri + return unless Gem.loaded_specs.key?('rubocop') + + "#{Gem.loaded_specs['rubocop'].metadata['bug_tracker_uri']}\n" + end + def maybe_print_corrected_source # Integration tools (like RubyMine) expect to have only the JSON result # when specifying JSON format. Similar HTML and JUnit are targeted as well. diff --git a/lib/rubocop/comment_config.rb b/lib/rubocop/comment_config.rb index fd4e3b0a18ca..3cab1bf181de 100644 --- a/lib/rubocop/comment_config.rb +++ b/lib/rubocop/comment_config.rb @@ -11,6 +11,8 @@ class CommentConfig # This class provides an API compatible with RuboCop::DirectiveComment # to be used for cops that are disabled in the config file class ConfigDisabledCopDirectiveComment + include RuboCop::Ext::Comment + attr_reader :text, :loc, :line_number Loc = Struct.new(:expression) @@ -42,6 +44,10 @@ def cop_enabled_at_line?(cop, line_number) disabled_line_ranges.none? { |range| range.include?(line_number) } end + def cop_opted_in?(cop) + opt_in_cops.include?(cop.cop_name) + end + def cop_disabled_line_ranges @cop_disabled_line_ranges ||= analyze end @@ -74,6 +80,19 @@ def extra_enabled_comments_with_names(extras:, names:) extras end + def opt_in_cops + @opt_in_cops ||= begin + cops = Set.new + each_directive do |directive| + next unless directive.enabled? + next if directive.all_cops? + + cops.merge(directive.cop_names) + end + cops + end + end + def analyze # rubocop:todo Metrics/AbcSize return {} if @no_directives diff --git a/lib/rubocop/config.rb b/lib/rubocop/config.rb index 48d69656824e..06a5f20516b2 100644 --- a/lib/rubocop/config.rb +++ b/lib/rubocop/config.rb @@ -29,7 +29,7 @@ def self.create(hash, path, check: true) end # rubocop:disable Metrics/AbcSize - def initialize(hash = {}, loaded_path = nil) + def initialize(hash = RuboCop::ConfigLoader.default_configuration, loaded_path = nil) @loaded_path = loaded_path @for_cop = Hash.new do |h, cop| cop_name = cop.respond_to?(:cop_name) ? cop.cop_name : cop @@ -304,8 +304,8 @@ def read_rails_version_from_bundler_lock_file end def enable_cop?(qualified_cop_name, cop_options) - # If the cop is explicitly enabled, the other checks can be skipped. - return true if cop_options['Enabled'] == true + # If the cop is explicitly enabled or `Lint/Syntax`, the other checks can be skipped. + return true if cop_options['Enabled'] == true || qualified_cop_name == 'Lint/Syntax' department = department_of(qualified_cop_name) cop_enabled = cop_options.fetch('Enabled') { !for_all_cops['DisabledByDefault'] } diff --git a/lib/rubocop/config_loader.rb b/lib/rubocop/config_loader.rb index 7e6c6a9617f3..a50e3ff0aaee 100644 --- a/lib/rubocop/config_loader.rb +++ b/lib/rubocop/config_loader.rb @@ -111,10 +111,17 @@ def configuration_from_file(config_file, check: true) end merge_with_default(config, config_file).tap do |merged_config| - warn_on_pending_cops(merged_config.pending_cops) unless possible_new_cops?(merged_config) + unless possible_new_cops?(merged_config) + pending_cops = pending_cops_only_qualified(merged_config.pending_cops) + warn_on_pending_cops(pending_cops) unless pending_cops.empty? + end end end + def pending_cops_only_qualified(pending_cops) + pending_cops.select { |cop| Cop::Registry.qualified_cop?(cop.name) } + end + def possible_new_cops?(config) disable_pending_cops || enable_pending_cops || config.disabled_new_cops? || config.enabled_new_cops? @@ -167,8 +174,6 @@ def project_root BANNER def warn_on_pending_cops(pending_cops) - return if pending_cops.empty? - warn Rainbow(PENDING_BANNER).yellow pending_cops.each { |cop| warn_pending_cop cop } @@ -241,18 +246,10 @@ def yaml_safe_load(yaml_code, filename) raise end - if Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.1.0') - def yaml_safe_load!(yaml_code, filename) - YAML.safe_load(yaml_code, - permitted_classes: [Regexp, Symbol], - permitted_symbols: [], - aliases: true, - filename: filename) - end - else # Ruby < 2.6 - def yaml_safe_load!(yaml_code, filename) - YAML.safe_load(yaml_code, [Regexp, Symbol], [], true, filename) - end + def yaml_safe_load!(yaml_code, filename) + YAML.safe_load( + yaml_code, permitted_classes: [Regexp, Symbol], aliases: true, filename: filename + ) end end diff --git a/lib/rubocop/config_loader_resolver.rb b/lib/rubocop/config_loader_resolver.rb index 5e433c3d35e4..045171950fb9 100644 --- a/lib/rubocop/config_loader_resolver.rb +++ b/lib/rubocop/config_loader_resolver.rb @@ -33,7 +33,7 @@ def resolve_inheritance(path, hash, file, debug) # rubocop:disable Metrics/Metho inherit_mode: determine_inherit_mode(hash, k)) end hash[k] = v - fix_include_paths(base_config.loaded_path, hash, path, k, v) if v.key?('Include') + fix_include_paths(base_config.loaded_path, hash, k, v) if v.key?('Include') end end end @@ -42,13 +42,12 @@ def resolve_inheritance(path, hash, file, debug) # rubocop:disable Metrics/Metho # base configuration are relative to the directory where the base configuration file is. For the # derived configuration, we need to make those paths relative to where the derived configuration # file is. - def fix_include_paths(base_config_path, hash, path, key, value) + def fix_include_paths(base_config_path, hash, key, value) return unless File.basename(base_config_path).start_with?('.rubocop') base_dir = File.dirname(base_config_path) - derived_dir = File.dirname(path) hash[key]['Include'] = value['Include'].map do |include_path| - PathUtil.relative_path(File.join(base_dir, include_path), derived_dir) + PathUtil.relative_path(File.join(base_dir, include_path), Dir.pwd) end end diff --git a/lib/rubocop/cop/autocorrect_logic.rb b/lib/rubocop/cop/autocorrect_logic.rb index da2a2475edfb..a645ff5fe0b5 100644 --- a/lib/rubocop/cop/autocorrect_logic.rb +++ b/lib/rubocop/cop/autocorrect_logic.rb @@ -35,7 +35,7 @@ def autocorrect_enabled? return false if cop_config['AutoCorrect'] == false # :safe_autocorrect is a derived option based on several command-line - # arguments - see Rubocop::Options#add_autocorrection_options + # arguments - see RuboCop::Options#add_autocorrection_options return safe_autocorrect? if @options.fetch(:safe_autocorrect, false) true @@ -43,18 +43,24 @@ def autocorrect_enabled? private - def disable_offense(range) - heredoc_range = surrounding_heredoc(range) - if heredoc_range - disable_offense_before_and_after(range_by_lines(heredoc_range)) + def disable_offense(offense_range) + range = surrounding_heredoc(offense_range) || surrounding_percent_array(offense_range) + + if range + disable_offense_before_and_after(range_by_lines(range)) + else + disable_offense_with_eol_or_surround_comment(offense_range) + end + end + + def disable_offense_with_eol_or_surround_comment(range) + eol_comment = " # rubocop:todo #{cop_name}" + needed_line_length = (range.source_line + eol_comment).length + + if needed_line_length <= max_line_length + disable_offense_at_end_of_line(range_of_first_line(range), eol_comment) else - eol_comment = " # rubocop:todo #{cop_name}" - needed_line_length = (range.source_line + eol_comment).length - if needed_line_length <= max_line_length - disable_offense_at_end_of_line(range_of_first_line(range), eol_comment) - else - disable_offense_before_and_after(range_by_lines(range)) - end + disable_offense_before_and_after(range_by_lines(range)) end end @@ -65,10 +71,20 @@ def surrounding_heredoc(offense_range) heredoc_nodes = processed_source.ast.each_descendant.select do |node| node.respond_to?(:heredoc?) && node.heredoc? end - heredoc_nodes.map { |node| node.loc.expression.join(node.loc.heredoc_end) } + heredoc_nodes.map { |node| node.source_range.join(node.loc.heredoc_end) } .find { |range| range.contains?(offense_range) } end + def surrounding_percent_array(offense_range) + return nil if offense_range.empty? + + percent_array = processed_source.ast.each_descendant.select do |node| + node.array_type? && node.percent_literal? + end + + percent_array.map(&:source_range).find { |range| range.overlaps?(offense_range) } + end + def range_of_first_line(range) begin_of_first_line = range.begin_pos - range.column end_of_first_line = begin_of_first_line + range.source_line.length diff --git a/lib/rubocop/cop/base.rb b/lib/rubocop/cop/base.rb index 7e277188e60b..119d6759db63 100644 --- a/lib/rubocop/cop/base.rb +++ b/lib/rubocop/cop/base.rb @@ -180,6 +180,10 @@ def add_offense(node_or_range, message: nil, severity: nil, &block) status, corrector = enabled_line?(range.line) ? correct(range, &block) : :disabled + # Since this range may be generated from Ruby code embedded in some + # template file, we convert it to location info in the original file. + range = range_for_original(range) + current_offenses << Offense.new(severity, range, message, name, status, corrector) end @@ -286,6 +290,21 @@ def self.callbacks_needed end # rubocop:enable Layout/ClassStructure + # Called before any investigation + # @api private + def begin_investigation(processed_source, offset: 0, original: processed_source) + @current_offenses = nil + @current_offense_locations = nil + @currently_disabled_lines = nil + @processed_source = processed_source + @current_corrector = nil + + # We need to keep track of the original source and offset, + # because `processed_source` here may be an embedded code in it. + @current_offset = offset + @current_original = original + end + private ### Reserved for Cop::Cop @@ -320,15 +339,6 @@ def current_offenses @restrict_on_send ||= self::RESTRICT_ON_SEND.to_a.freeze end - # Called before any investigation - def begin_investigation(processed_source) - @current_offenses = nil - @current_offense_locations = nil - @currently_disabled_lines = nil - @processed_source = processed_source - @current_corrector = nil - end - EMPTY_OFFENSES = [].freeze private_constant :EMPTY_OFFENSES # Called to complete an investigation @@ -406,7 +416,7 @@ def disable_uncorrectable(range) def range_from_node_or_range(node_or_range) if node_or_range.respond_to?(:loc) - node_or_range.loc.expression + node_or_range.source_range elsif node_or_range.is_a?(::Parser::Source::Range) node_or_range else @@ -459,6 +469,14 @@ def custom_severity warn(Rainbow(message).red) end end + + def range_for_original(range) + ::Parser::Source::Range.new( + @current_original.buffer, + range.begin_pos + @current_offset, + range.end_pos + @current_offset + ) + end end end end diff --git a/lib/rubocop/cop/bundler/gem_comment.rb b/lib/rubocop/cop/bundler/gem_comment.rb index 90757ecb3cd9..590244a1fd7a 100644 --- a/lib/rubocop/cop/bundler/gem_comment.rb +++ b/lib/rubocop/cop/bundler/gem_comment.rb @@ -124,7 +124,7 @@ def preceding_lines(node) end def preceding_comment?(node1, node2) - node1 && node2 && precede?(node2, node1) && comment_line?(node2.loc.expression.source) + node1 && node2 && precede?(node2, node1) && comment_line?(node2.source) end def ignored_gem?(node) diff --git a/lib/rubocop/cop/commissioner.rb b/lib/rubocop/cop/commissioner.rb index d40fefcc3f43..cd3208425dd4 100644 --- a/lib/rubocop/cop/commissioner.rb +++ b/lib/rubocop/cop/commissioner.rb @@ -76,10 +76,10 @@ def on_#{node_type}(node) # def on_send(node) end # @return [InvestigationReport] - def investigate(processed_source) + def investigate(processed_source, offset: 0, original: processed_source) reset - @cops.each { |cop| cop.send :begin_investigation, processed_source } + begin_investigation(processed_source, offset: offset, original: original) if processed_source.valid_syntax? invoke(:on_new_investigation, @cops) invoke_with_argument(:investigate, @forces, processed_source) @@ -95,6 +95,12 @@ def investigate(processed_source) private + def begin_investigation(processed_source, offset:, original:) + @cops.each do |cop| + cop.begin_investigation(processed_source, offset: offset, original: original) + end + end + def trigger_responding_cops(callback, node) @callbacks[callback]&.each do |cop| with_cop_error_handling(cop, node) do diff --git a/lib/rubocop/cop/cop.rb b/lib/rubocop/cop/cop.rb index 2bd5391ce120..9ba432a5435c 100644 --- a/lib/rubocop/cop/cop.rb +++ b/lib/rubocop/cop/cop.rb @@ -53,6 +53,11 @@ def self.qualified_cop_name(name, origin) def add_offense(node_or_range, location: :expression, message: nil, severity: nil, &block) @v0_argument = node_or_range range = find_location(node_or_range, location) + + # Since this range may be generated from Ruby code embedded in some + # template file, we convert it to location info in the original file. + range = range_for_original(range) + if block.nil? && !support_autocorrect? super(range, message: message, severity: severity) else @@ -93,14 +98,21 @@ def on_investigation_end super end - private - - def begin_investigation(processed_source) + # Called before any investigation + # @api private + def begin_investigation(processed_source, offset: 0, original: processed_source) super @offenses = current_offenses @last_corrector = @current_corrector + + # We need to keep track of the original source and offset, + # because `processed_source` here may be an embedded code in it. + @current_offset = offset + @current_original = original end + private + # Override Base def callback_argument(_range) @v0_argument @@ -126,10 +138,10 @@ def emulate_v0_callsequence(corrector) def correction_lambda return unless support_autocorrect? - dedup_on_node(@v0_argument) { autocorrect(@v0_argument) } + dedupe_on_node(@v0_argument) { autocorrect(@v0_argument) } end - def dedup_on_node(node) + def dedupe_on_node(node) @corrected_nodes ||= {}.compare_by_identity yield unless @corrected_nodes.key?(node) ensure @@ -141,6 +153,14 @@ def suppress_clobbering rescue ::Parser::ClobberingError # ignore Clobbering errors end + + def range_for_original(range) + ::Parser::Source::Range.new( + @current_original.buffer, + range.begin_pos + @current_offset, + range.end_pos + @current_offset + ) + end end end end diff --git a/lib/rubocop/cop/corrector.rb b/lib/rubocop/cop/corrector.rb index b66c4dd03360..cb5828822e4a 100644 --- a/lib/rubocop/cop/corrector.rb +++ b/lib/rubocop/cop/corrector.rb @@ -86,8 +86,16 @@ def swap(node_or_range1, node_or_range2) range1 = to_range(node_or_range1) range2 = to_range(node_or_range2) - replace(range1, range2.source) - replace(range2, range1.source) + if range1.end_pos == range2.begin_pos + insert_before(range1, range2.source) + remove(range2) + elsif range2.end_pos == range1.begin_pos + insert_before(range2, range1.source) + remove(range1) + else + replace(range1, range2.source) + replace(range2, range1.source) + end end private @@ -96,7 +104,7 @@ def swap(node_or_range1, node_or_range2) def to_range(node_or_range) range = case node_or_range when ::RuboCop::AST::Node, ::Parser::Source::Comment - node_or_range.loc.expression + node_or_range.source_range when ::Parser::Source::Range node_or_range else diff --git a/lib/rubocop/cop/correctors/alignment_corrector.rb b/lib/rubocop/cop/correctors/alignment_corrector.rb index 1594a60fc276..33ffccd6efd8 100644 --- a/lib/rubocop/cop/correctors/alignment_corrector.rb +++ b/lib/rubocop/cop/correctors/alignment_corrector.rb @@ -16,7 +16,7 @@ def correct(corrector, processed_source, node, column_delta) return unless node @processed_source = processed_source - expr = node.respond_to?(:loc) ? node.loc.expression : node + expr = node.respond_to?(:loc) ? node.source_range : node return if block_comment_within?(expr) taboo_ranges = inside_string_ranges(node) @@ -80,7 +80,7 @@ def delimited_string_literal?(node) def block_comment_within?(expr) processed_source.comments.select(&:document?).any? do |c| - within?(c.loc.expression, expr) + within?(c.source_range, expr) end end diff --git a/lib/rubocop/cop/correctors/each_to_for_corrector.rb b/lib/rubocop/cop/correctors/each_to_for_corrector.rb index aa1d299cc070..4763a491c8f7 100644 --- a/lib/rubocop/cop/correctors/each_to_for_corrector.rb +++ b/lib/rubocop/cop/correctors/each_to_for_corrector.rb @@ -35,15 +35,15 @@ def correction def offending_range if block_node.arguments? - replacement_range(argument_node.loc.expression.end_pos) + replacement_range(argument_node.source_range.end_pos) else replacement_range(block_node.loc.begin.end_pos) end end def replacement_range(end_pos) - Parser::Source::Range.new(block_node.loc.expression.source_buffer, - block_node.loc.expression.begin_pos, + Parser::Source::Range.new(block_node.source_range.source_buffer, + block_node.source_range.begin_pos, end_pos) end end diff --git a/lib/rubocop/cop/correctors/for_to_each_corrector.rb b/lib/rubocop/cop/correctors/for_to_each_corrector.rb index e54fecd7af87..4f711c49bc5a 100644 --- a/lib/rubocop/cop/correctors/for_to_each_corrector.rb +++ b/lib/rubocop/cop/correctors/for_to_each_corrector.rb @@ -56,7 +56,7 @@ def collection_end if collection_node.begin_type? collection_node.loc.end else - collection_node.loc.expression + collection_node.source_range end end @@ -65,8 +65,8 @@ def offending_range end def replacement_range(end_pos) - Parser::Source::Range.new(for_node.loc.expression.source_buffer, - for_node.loc.expression.begin_pos, + Parser::Source::Range.new(for_node.source_range.source_buffer, + for_node.source_range.begin_pos, end_pos) end end diff --git a/lib/rubocop/cop/correctors/line_break_corrector.rb b/lib/rubocop/cop/correctors/line_break_corrector.rb index 73e163daad8c..b5a14c7e4549 100644 --- a/lib/rubocop/cop/correctors/line_break_corrector.rb +++ b/lib/rubocop/cop/correctors/line_break_corrector.rb @@ -35,7 +35,7 @@ def break_line_before(range:, node:, corrector:, configured_width:, def move_comment(eol_comment:, node:, corrector:) return unless eol_comment - text = eol_comment.loc.expression.source + text = eol_comment.source corrector.insert_before(node, "#{text}\n#{' ' * node.loc.keyword.column}") corrector.remove(eol_comment) end diff --git a/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb b/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb index b2e1a478df18..97f841ebaa4a 100644 --- a/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +++ b/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb @@ -52,7 +52,7 @@ def correct_heredoc_argument_method_chain(corrector, end_range) return unless (parent = node.parent) return unless use_heredoc_argument_method_chain?(parent) - chained_method = range_between(parent.loc.dot.begin_pos, parent.loc.expression.end_pos) + chained_method = range_between(parent.loc.dot.begin_pos, parent.source_range.end_pos) corrector.remove(chained_method) corrector.insert_after(end_range, chained_method.source) @@ -80,7 +80,7 @@ def use_heredoc_argument_method_chain?(parent) def select_content_to_be_inserted_after_last_element(corrector, node) range = range_between( node.loc.end.begin_pos, - range_by_whole_lines(node.loc.expression).end.end_pos + range_by_whole_lines(node.source_range).end.end_pos ) remove_trailing_content_of_comment(corrector, range) diff --git a/lib/rubocop/cop/correctors/ordered_gem_corrector.rb b/lib/rubocop/cop/correctors/ordered_gem_corrector.rb index 8d8e0c55934a..35dde1d2da6d 100644 --- a/lib/rubocop/cop/correctors/ordered_gem_corrector.rb +++ b/lib/rubocop/cop/correctors/ordered_gem_corrector.rb @@ -18,7 +18,7 @@ def correct(processed_source, node, current_range = declaration_with_comment(node) previous_range = declaration_with_comment(previous_declaration) - ->(corrector) { swap_range(corrector, current_range, previous_range) } + ->(corrector) { corrector.swap(current_range, previous_range) } end private @@ -26,17 +26,12 @@ def correct(processed_source, node, def declaration_with_comment(node) buffer = processed_source.buffer begin_pos = range_by_whole_lines(get_source_range(node, comments_as_separators)).begin_pos - end_line = buffer.line_for_position(node.loc.expression.end_pos) + end_line = buffer.line_for_position(node.source_range.end_pos) end_pos = range_by_whole_lines(buffer.line_range(end_line), include_final_newline: true).end_pos range_between(begin_pos, end_pos) end - - def swap_range(corrector, range1, range2) - corrector.insert_before(range2, range1.source) - corrector.remove(range1) - end end end end diff --git a/lib/rubocop/cop/correctors/parentheses_corrector.rb b/lib/rubocop/cop/correctors/parentheses_corrector.rb index 71e01b60641b..9533fe49ce82 100644 --- a/lib/rubocop/cop/correctors/parentheses_corrector.rb +++ b/lib/rubocop/cop/correctors/parentheses_corrector.rb @@ -74,7 +74,7 @@ def extend_range_for_heredoc(node, range) def add_heredoc_comma(corrector, node) return unless heredoc?(node) - corrector.insert_after(node.child_nodes.last.loc.expression, ',') + corrector.insert_after(node.child_nodes.last, ',') end def heredoc?(node) diff --git a/lib/rubocop/cop/correctors/percent_literal_corrector.rb b/lib/rubocop/cop/correctors/percent_literal_corrector.rb index 769ad50c1874..b6bb00b121de 100644 --- a/lib/rubocop/cop/correctors/percent_literal_corrector.rb +++ b/lib/rubocop/cop/correctors/percent_literal_corrector.rb @@ -66,10 +66,10 @@ def process_multiline_words(node, escape, delimiters) end end - def line_breaks(node, source, previous_line_num, base_line_num, node_indx) + def line_breaks(node, source, previous_line_num, base_line_num, node_index) source_in_lines = source.split("\n") if first_line?(node, previous_line_num) - node_indx.zero? && node.first_line == base_line_num ? '' : ' ' + node_index.zero? && node.first_line == base_line_num ? '' : ' ' else process_lines(node, previous_line_num, base_line_num, source_in_lines) end diff --git a/lib/rubocop/cop/gemspec/dependency_version.rb b/lib/rubocop/cop/gemspec/dependency_version.rb index 4dde1f87d49f..679eaf5311b4 100644 --- a/lib/rubocop/cop/gemspec/dependency_version.rb +++ b/lib/rubocop/cop/gemspec/dependency_version.rb @@ -94,7 +94,7 @@ def on_send(node) private def allowed_gem?(node) - allowed_gems.include?(node.first_argument.value) + allowed_gems.include?(node.first_argument.str_content) end def allowed_gems diff --git a/lib/rubocop/cop/gemspec/development_dependencies.rb b/lib/rubocop/cop/gemspec/development_dependencies.rb new file mode 100644 index 000000000000..6aec200befa4 --- /dev/null +++ b/lib/rubocop/cop/gemspec/development_dependencies.rb @@ -0,0 +1,107 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Gemspec + # Enforce that development dependencies for a gem are specified in + # `Gemfile`, rather than in the `gemspec` using + # `add_development_dependency`. Alternatively, using `EnforcedStyle: + # gemspec`, enforce that all dependencies are specified in `gemspec`, + # rather than in `Gemfile`. + # + # @example EnforcedStyle: Gemfile (default) + # # Specify runtime dependencies in your gemspec, + # # but all other dependencies in your Gemfile. + # + # # bad + # # example.gemspec + # s.add_development_dependency "foo" + # + # # good + # # Gemfile + # gem "foo" + # + # # good + # # gems.rb + # gem "foo" + # + # # good (with AllowedGems: ["bar"]) + # # example.gemspec + # s.add_development_dependency "bar" + # + # @example EnforcedStyle: gems.rb + # # Specify runtime dependencies in your gemspec, + # # but all other dependencies in your Gemfile. + # # + # # Identical to `EnforcedStyle: Gemfile`, but with a different error message. + # # Rely on Bundler/GemFilename to enforce the use of `Gemfile` vs `gems.rb`. + # + # # bad + # # example.gemspec + # s.add_development_dependency "foo" + # + # # good + # # Gemfile + # gem "foo" + # + # # good + # # gems.rb + # gem "foo" + # + # # good (with AllowedGems: ["bar"]) + # # example.gemspec + # s.add_development_dependency "bar" + # + # @example EnforcedStyle: gemspec + # # Specify all dependencies in your gemspec. + # + # # bad + # # Gemfile + # gem "foo" + # + # # good + # # example.gemspec + # s.add_development_dependency "foo" + # + # # good (with AllowedGems: ["bar"]) + # # Gemfile + # gem "bar" + # + class DevelopmentDependencies < Base + include ConfigurableEnforcedStyle + + MSG = 'Specify development dependencies in %s.' + RESTRICT_ON_SEND = %i[add_development_dependency gem].freeze + + # @!method add_development_dependency?(node) + def_node_matcher :add_development_dependency?, <<~PATTERN + (send _ :add_development_dependency (str #forbidden_gem? ...)) + PATTERN + + # @!method gem?(node) + def_node_matcher :gem?, <<~PATTERN + (send _ :gem (str #forbidden_gem? ...)) + PATTERN + + def on_send(node) + case style + when :Gemfile, :'gems.rb' + add_offense(node) if add_development_dependency?(node) + when :gemspec + add_offense(node) if gem?(node) + end + end + + private + + def forbidden_gem?(gem_name) + !cop_config['AllowedGems'].include?(gem_name) + end + + def message(_range) + format(MSG, preferred: style) + end + end + end + end +end diff --git a/lib/rubocop/cop/internal_affairs.rb b/lib/rubocop/cop/internal_affairs.rb index e311e66cd09c..504a73fba3dd 100644 --- a/lib/rubocop/cop/internal_affairs.rb +++ b/lib/rubocop/cop/internal_affairs.rb @@ -7,6 +7,7 @@ require_relative 'internal_affairs/example_heredoc_delimiter' require_relative 'internal_affairs/inherit_deprecated_cop_class' require_relative 'internal_affairs/lambda_or_proc' +require_relative 'internal_affairs/location_expression' require_relative 'internal_affairs/location_line_equality_comparison' require_relative 'internal_affairs/method_name_end_with' require_relative 'internal_affairs/method_name_equal' @@ -15,12 +16,14 @@ require_relative 'internal_affairs/node_type_predicate' require_relative 'internal_affairs/numblock_handler' require_relative 'internal_affairs/offense_location_keyword' +require_relative 'internal_affairs/processed_source_buffer_name' require_relative 'internal_affairs/redundant_context_config_parameter' require_relative 'internal_affairs/redundant_described_class_as_subject' require_relative 'internal_affairs/redundant_let_rubocop_config_new' require_relative 'internal_affairs/redundant_location_argument' require_relative 'internal_affairs/redundant_message_argument' require_relative 'internal_affairs/redundant_method_dispatch_node' +require_relative 'internal_affairs/redundant_source_range' require_relative 'internal_affairs/single_line_comparison' require_relative 'internal_affairs/style_detected_api_use' require_relative 'internal_affairs/undefined_config' diff --git a/lib/rubocop/cop/internal_affairs/cop_description.rb b/lib/rubocop/cop/internal_affairs/cop_description.rb index dfae58857a35..cdeb3e672750 100644 --- a/lib/rubocop/cop/internal_affairs/cop_description.rb +++ b/lib/rubocop/cop/internal_affairs/cop_description.rb @@ -24,7 +24,7 @@ class CopDescription < Base MSG = 'Description should be started with %s instead of `This cop ...`.' SPECIAL_WORDS = %w[is can could should will would must may].freeze - COP_DESC_OFFENSE_REGEX = \ + COP_DESC_OFFENSE_REGEX = /^\s+# This cop (?#{SPECIAL_WORDS.join('|')})?\s*(?.+?) .*/.freeze REPLACEMENT_REGEX = /^\s+# This cop (#{SPECIAL_WORDS.join('|')})?\s*(.+?) /.freeze @@ -59,9 +59,9 @@ def replace_with_suggestion(corrector, range, suggestion, description_beginning) end def range(node, comment_line) - source_buffer = node.loc.expression.source_buffer + source_buffer = node.source_range.source_buffer - begin_pos = node.loc.expression.begin_pos + begin_pos = node.source_range.begin_pos begin_pos += comment_index(node, comment_line) end_pos = begin_pos + comment_body(comment_line).length @@ -77,7 +77,7 @@ def suggestion_for_message(suggestion, match_data) end def first_comment_line(node) - node.loc.expression.source.lines.find { |line| comment_line?(line) } + node.source.lines.find { |line| comment_line?(line) } end def comment_body(comment_line) @@ -86,7 +86,7 @@ def comment_body(comment_line) def comment_index(node, comment_line) body = comment_body(comment_line) - node.loc.expression.source.index(body) + node.source.index(body) end def relevant_file?(file) diff --git a/lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb b/lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb index 1e0f3b244f0e..78832673576b 100644 --- a/lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb +++ b/lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb @@ -52,7 +52,7 @@ def on_send(node) # @return [void] def autocorrect(corrector, node) [ - heredoc_openning_delimiter_range_from(node), + heredoc_opening_delimiter_range_from(node), heredoc_closing_delimiter_range_from(node) ].each do |range| corrector.replace(range, EXPECTED_HEREDOC_DELIMITER) @@ -90,9 +90,9 @@ def heredoc_delimiter_string_from(node) # @param node [RuboCop::AST::StrNode] # @return [Parser::Source::Range] - def heredoc_openning_delimiter_range_from(node) + def heredoc_opening_delimiter_range_from(node) match_data = node.source.match(Heredoc::OPENING_DELIMITER) - node.location.expression.begin.adjust( + node.source_range.begin.adjust( begin_pos: match_data.begin(2), end_pos: match_data.end(2) ) diff --git a/lib/rubocop/cop/internal_affairs/inherit_deprecated_cop_class.rb b/lib/rubocop/cop/internal_affairs/inherit_deprecated_cop_class.rb index a11e33b3de72..f7389e7940ba 100644 --- a/lib/rubocop/cop/internal_affairs/inherit_deprecated_cop_class.rb +++ b/lib/rubocop/cop/internal_affairs/inherit_deprecated_cop_class.rb @@ -3,7 +3,7 @@ module RuboCop module Cop module InternalAffairs - # `RuboCop::Cop::Cop` is deprecated and will be removed in Rubocop 2.0. + # `RuboCop::Cop::Cop` is deprecated and will be removed in RuboCop 2.0. # Your custom cop class should inherit from `RuboCop::Cop::Base` instead of # `RuboCop::Cop::Cop`. # diff --git a/lib/rubocop/cop/internal_affairs/location_expression.rb b/lib/rubocop/cop/internal_affairs/location_expression.rb new file mode 100644 index 000000000000..ad3c64a8f748 --- /dev/null +++ b/lib/rubocop/cop/internal_affairs/location_expression.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module InternalAffairs + # Enforces the use of `node.source_range` instead of `node.location.expression`. + # + # @example + # + # # bad + # node.location.expression + # node.loc.expression + # + # # good + # node.source_range + # + class LocationExpression < Base + extend AutoCorrector + + MSG = 'Use `source_range` instead.' + RESTRICT_ON_SEND = %i[loc location].freeze + + def on_send(node) + return unless (parent = node.parent) + return unless parent.send_type? && parent.method?(:expression) + return unless parent.receiver.receiver + + offense = node.loc.selector.join(parent.source_range.end) + + add_offense(offense) do |corrector| + corrector.replace(offense, 'source_range') + end + end + end + end + end +end diff --git a/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb b/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb index d04e7d4f5ae6..b88ef9d5dea4 100644 --- a/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +++ b/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb @@ -99,7 +99,7 @@ def insert_directive(corrector, node, actual_name) # If the pattern matcher uses arguments (`%1`, `%2`, etc.), include them in the directive arguments = pattern_arguments(node.arguments[1].source) - range = range_with_surrounding_space(node.loc.expression, side: :left, newlines: false) + range = range_with_surrounding_space(node.source_range, side: :left, newlines: false) indentation = range.source.match(/^\s*/)[0] directive = "#{indentation}# @!method #{actual_name}(#{arguments.join(', ')})\n" directive = "\n#{directive}" if add_newline?(node) diff --git a/lib/rubocop/cop/internal_affairs/node_type_predicate.rb b/lib/rubocop/cop/internal_affairs/node_type_predicate.rb index ac188c50e332..0460276218cf 100644 --- a/lib/rubocop/cop/internal_affairs/node_type_predicate.rb +++ b/lib/rubocop/cop/internal_affairs/node_type_predicate.rb @@ -30,7 +30,7 @@ def on_send(node) message = format(MSG, type: node_type) add_offense(node, message: message) do |corrector| - range = node.loc.expression.with(begin_pos: receiver.loc.expression.end_pos + 1) + range = node.source_range.with(begin_pos: receiver.source_range.end_pos + 1) corrector.replace(range, "#{node_type}_type?") end end diff --git a/lib/rubocop/cop/internal_affairs/processed_source_buffer_name.rb b/lib/rubocop/cop/internal_affairs/processed_source_buffer_name.rb new file mode 100644 index 000000000000..96d1eee48e92 --- /dev/null +++ b/lib/rubocop/cop/internal_affairs/processed_source_buffer_name.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module InternalAffairs + # Enforces the use of `processed_source.file_path` instead of `processed_source.buffer.name`. + # + # @example + # + # # bad + # processed_source.buffer.name + # + # # good + # processed_source.file_path + # + class ProcessedSourceBufferName < Base + extend AutoCorrector + + MSG = 'Use `file_path` instead.' + + RESTRICT_ON_SEND = %i[name].freeze + + # @!method processed_source_buffer_name?(node) + def_node_matcher :processed_source_buffer_name?, <<~PATTERN + (send + (send + {(lvar :processed_source) (send nil? :processed_source)} :buffer) :name) + PATTERN + + def on_send(node) + return unless processed_source_buffer_name?(node) + + offense_range = node.children.first.loc.selector.begin.join(node.source_range.end) + + add_offense(offense_range) do |corrector| + corrector.replace(offense_range, 'file_path') + end + end + end + end + end +end diff --git a/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb b/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb index 7a7deb112e7f..60ee490dfd54 100644 --- a/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +++ b/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb @@ -31,9 +31,17 @@ class RedundantLetRuboCopConfigNew < Base (send nil? :let (sym :config)) (args) - (send - (const - (const nil? :RuboCop) :Config) :new)) + { + (send + (const + (const nil? :RuboCop) :Config) :new) + (send + (const + (const nil? :RuboCop) :Config) :new + (hash (pair (send (send (send nil? :described_class) :badge) :to_s) + (send nil? :cop_config)))) + } + ) PATTERN def on_block(node) diff --git a/lib/rubocop/cop/internal_affairs/redundant_location_argument.rb b/lib/rubocop/cop/internal_affairs/redundant_location_argument.rb index ad9fa3392f8d..1ea114968d80 100644 --- a/lib/rubocop/cop/internal_affairs/redundant_location_argument.rb +++ b/lib/rubocop/cop/internal_affairs/redundant_location_argument.rb @@ -43,7 +43,7 @@ def on_send(node) private def offending_range(node) - with_space = range_with_surrounding_space(node.loc.expression) + with_space = range_with_surrounding_space(node.source_range) range_with_surrounding_comma(with_space, :left) end diff --git a/lib/rubocop/cop/internal_affairs/redundant_message_argument.rb b/lib/rubocop/cop/internal_affairs/redundant_message_argument.rb index a8ac477b4fed..9d4cc2569d00 100644 --- a/lib/rubocop/cop/internal_affairs/redundant_message_argument.rb +++ b/lib/rubocop/cop/internal_affairs/redundant_message_argument.rb @@ -56,7 +56,7 @@ def on_send(node) private def offending_range(node) - with_space = range_with_surrounding_space(node.loc.expression) + with_space = range_with_surrounding_space(node.source_range) range_with_surrounding_comma(with_space, :left) end diff --git a/lib/rubocop/cop/internal_affairs/redundant_source_range.rb b/lib/rubocop/cop/internal_affairs/redundant_source_range.rb new file mode 100644 index 000000000000..eb0d7b8a1ad1 --- /dev/null +++ b/lib/rubocop/cop/internal_affairs/redundant_source_range.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module InternalAffairs + # Checks for redundant `source_range`. + # + # @example + # + # # bad + # node.source_range.source + # + # # good + # node.source + # + # # bad + # add_offense(node) { |corrector| corrector.replace(node.source_range, prefer) } + # add_offense(node) { |corrector| corrector.insert_before(node.source_range, prefer) } + # add_offense(node) { |corrector| corrector.insert_before_multi(node.source_range, prefer) } + # add_offense(node) { |corrector| corrector.insert_after(node.source_range, prefer) } + # add_offense(node) { |corrector| corrector.insert_after_multi(node.source_range, prefer) } + # add_offense(node) { |corrector| corrector.swap(node.source_range, before, after) } + # + # # good + # add_offense(node) { |corrector| corrector.replace(node, prefer) } + # add_offense(node) { |corrector| corrector.insert_before(node, prefer) } + # add_offense(node) { |corrector| corrector.insert_before_multi(node, prefer) } + # add_offense(node) { |corrector| corrector.insert_after(node, prefer) } + # add_offense(node) { |corrector| corrector.insert_after_multi(node, prefer) } + # add_offense(node) { |corrector| corrector.swap(node, before, after) } + # + class RedundantSourceRange < Base + extend AutoCorrector + + MSG = 'Remove the redundant `source_range`.' + RESTRICT_ON_SEND = %i[ + source + replace remove insert_before insert_before_multi insert_after insert_after_multi swap + ].freeze + + # @!method redundant_source_range(node) + def_node_matcher :redundant_source_range, <<~PATTERN + { + (send $(send _ :source_range) :source) + (send _ { + :replace :insert_before :insert_before_multi :insert_after :insert_after_multi + } $(send _ :source_range) _) + (send _ :remove $(send _ :source_range)) + (send _ :swap $(send _ :source_range) _ _) + } + PATTERN + + def on_send(node) + return unless (source_range = redundant_source_range(node)) + return if source_range.receiver.send_type? && source_range.receiver.method?(:buffer) + + selector = source_range.loc.selector + + add_offense(selector) do |corrector| + corrector.remove(source_range.loc.dot.join(selector)) + end + end + end + end + end +end diff --git a/lib/rubocop/cop/layout/array_alignment.rb b/lib/rubocop/cop/layout/array_alignment.rb index 69c5acf36c5a..d93225a8410b 100644 --- a/lib/rubocop/cop/layout/array_alignment.rb +++ b/lib/rubocop/cop/layout/array_alignment.rb @@ -76,7 +76,7 @@ def base_column(node, args) end def target_method_lineno(node) - node.loc.line + node.bracketed? ? node.loc.line : node.parent.loc.line end end end diff --git a/lib/rubocop/cop/layout/block_end_newline.rb b/lib/rubocop/cop/layout/block_end_newline.rb index 595eb684a7eb..257ebc1787de 100644 --- a/lib/rubocop/cop/layout/block_end_newline.rb +++ b/lib/rubocop/cop/layout/block_end_newline.rb @@ -36,17 +36,19 @@ def on_block(node) # If the end is on its own line, there is no offense return if begins_its_line?(node.loc.end) - register_offense(node) + offense_range = offense_range(node) + return if offense_range.source.lstrip.start_with?(';') + + register_offense(node, offense_range) end alias on_numblock on_block private - def register_offense(node) + def register_offense(node, offense_range) add_offense(node.loc.end, message: message(node)) do |corrector| - offense_range = offense_range(node) - replacement = "\n#{offense_range.source.strip}" + replacement = "\n#{offense_range.source.lstrip}" if (heredoc = last_heredoc_argument(node.body)) corrector.remove(offense_range) @@ -72,17 +74,7 @@ def last_heredoc_argument(node) end def offense_range(node) - Parser::Source::Range.new( - node.loc.expression.source_buffer, - node.children.compact.last.loc.expression.end_pos, - end_of_method_chain(node).loc.expression.end_pos - ) - end - - def end_of_method_chain(node) - return node unless node.parent&.call_type? - - end_of_method_chain(node.parent) + node.children.compact.last.source_range.end.join(node.loc.end) end end end diff --git a/lib/rubocop/cop/layout/class_structure.rb b/lib/rubocop/cop/layout/class_structure.rb index 26b807cf3229..a14b9a802d9d 100644 --- a/lib/rubocop/cop/layout/class_structure.rb +++ b/lib/rubocop/cop/layout/class_structure.rb @@ -134,6 +134,7 @@ module Layout # class ClassStructure < Base include VisibilityHelp + include CommentsHelp extend AutoCorrector HUMANIZED_NODE_TYPE = { @@ -163,7 +164,7 @@ def on_class(class_node) # Autocorrect by swapping between two nodes autocorrecting them def autocorrect(corrector, node) - previous = node.left_siblings.find do |sibling| + previous = node.left_siblings.reverse.find do |sibling| !ignore_for_autocorrect?(node, sibling) end return unless previous @@ -283,26 +284,13 @@ def marked_as_private_constant?(node, name) node.arguments.any? { |arg| (arg.sym_type? || arg.str_type?) && arg.value == name } end - def source_range_with_comment(node) - begin_pos, end_pos = - if (node.def_type? && !node.method?(:initialize)) || - (node.send_type? && node.def_modifier?) - start_node = find_visibility_start(node) || node - end_node = find_visibility_end(node) || node - [begin_pos_with_comment(start_node), - end_position_for(end_node) + 1] - else - [begin_pos_with_comment(node), end_position_for(node)] - end - - Parser::Source::Range.new(buffer, begin_pos, end_pos) - end - def end_position_for(node) - heredoc = find_heredoc(node) - return heredoc.location.heredoc_end.end_pos + 1 if heredoc + if node.casgn_type? + heredoc = find_heredoc(node) + return heredoc.location.heredoc_end.end_pos + 1 if heredoc + end - end_line = buffer.line_for_position(node.loc.expression.end_pos) + end_line = buffer.line_for_position(node.source_range.end_pos) buffer.line_range(end_line).end_pos end diff --git a/lib/rubocop/cop/layout/closing_heredoc_indentation.rb b/lib/rubocop/cop/layout/closing_heredoc_indentation.rb index 429cfb6f03bc..4367a83424c9 100644 --- a/lib/rubocop/cop/layout/closing_heredoc_indentation.rb +++ b/lib/rubocop/cop/layout/closing_heredoc_indentation.rb @@ -85,7 +85,7 @@ def closing_indentation(node) end def heredoc_opening(node) - node.loc.expression.source_line + node.source_range.source_line end def heredoc_closing(node) diff --git a/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb b/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb index 95b89d4b7e13..99d4bf043b21 100644 --- a/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +++ b/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb @@ -144,7 +144,7 @@ def check_for_no_elements(node) def expected_column(left_paren, elements) if line_break_after_left_paren?(left_paren, elements) source_indent = processed_source.line_indentation(first_argument_line(elements)) - new_indent = source_indent - indentation_width + new_indent = source_indent - configured_indentation_width new_indent.negative? ? 0 : new_indent elsif all_elements_aligned?(elements) @@ -157,7 +157,7 @@ def expected_column(left_paren, elements) def all_elements_aligned?(elements) elements.flat_map do |e| if e.hash_type? - e.each_pair.map { |pair| pair.loc.column } + e.each_child_node.map { |child| child.loc.column } else e.loc.column end @@ -184,10 +184,6 @@ def message(correct_column, left_paren, right_paren) end end - def indentation_width - @config.for_cop('Layout/IndentationWidth')['Width'] || 2 - end - def line_break_after_left_paren?(left_paren, elements) elements.first && elements.first.loc.line > left_paren.line end diff --git a/lib/rubocop/cop/layout/empty_comment.rb b/lib/rubocop/cop/layout/empty_comment.rb index c3d997ebf55e..a7dfeb7b92ff 100644 --- a/lib/rubocop/cop/layout/empty_comment.rb +++ b/lib/rubocop/cop/layout/empty_comment.rb @@ -97,9 +97,9 @@ def investigate(comments) def autocorrect(corrector, node) previous_token = previous_token(node) range = if previous_token && same_line?(node, previous_token) - range_with_surrounding_space(node.loc.expression, newlines: false) + range_with_surrounding_space(node.source_range, newlines: false) else - range_by_whole_lines(node.loc.expression, include_final_newline: true) + range_by_whole_lines(node.source_range, include_final_newline: true) end corrector.remove(range) @@ -137,7 +137,7 @@ def allow_margin_comment? end def current_token(comment) - processed_source.find_token { |token| token.pos == comment.loc.expression } + processed_source.find_token { |token| token.pos == comment.source_range } end def previous_token(node) diff --git a/lib/rubocop/cop/layout/empty_line_between_defs.rb b/lib/rubocop/cop/layout/empty_line_between_defs.rb index 51897cd7d8c7..715a609e74d6 100644 --- a/lib/rubocop/cop/layout/empty_line_between_defs.rb +++ b/lib/rubocop/cop/layout/empty_line_between_defs.rb @@ -235,7 +235,7 @@ def def_end(node) def end_loc(node) if (node.def_type? || node.defs_type?) && node.endless? - node.loc.expression.end + node.source_range.end else node.loc.end end diff --git a/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb b/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb index ecb17609b4ea..f780ffbcb13d 100644 --- a/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +++ b/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb @@ -51,6 +51,8 @@ class EmptyLinesAroundAccessModifier < Base MSG_BEFORE_FOR_ONLY_BEFORE = 'Keep a blank line before `%s`.' MSG_AFTER_FOR_ONLY_BEFORE = 'Remove a blank line after `%s`.' + RESTRICT_ON_SEND = %i[public protected private module_function].freeze + def initialize(config = nil, options = nil) super diff --git a/lib/rubocop/cop/layout/end_alignment.rb b/lib/rubocop/cop/layout/end_alignment.rb index 312d0f6a4b5f..6e75b78593a1 100644 --- a/lib/rubocop/cop/layout/end_alignment.rb +++ b/lib/rubocop/cop/layout/end_alignment.rb @@ -82,6 +82,10 @@ def on_class(node) check_other_alignment(node) end + def on_sclass(node) + check_other_alignment(node) + end + def on_module(node) check_other_alignment(node) end @@ -105,6 +109,7 @@ def on_case(node) check_other_alignment(node) end end + alias on_case_match on_case private @@ -165,7 +170,10 @@ def alignment_node(node) end def alignment_node_for_variable_style(node) - return node.parent if node.case_type? && node.argument? && same_line?(node, node.parent) + if (node.case_type? || node.case_match_type?) && node.argument? && + same_line?(node, node.parent) + return node.parent + end assignment = assignment_or_operator_method(node) diff --git a/lib/rubocop/cop/layout/extra_spacing.rb b/lib/rubocop/cop/layout/extra_spacing.rb index 0d7ea2fb9bf1..2281542a5fb6 100644 --- a/lib/rubocop/cop/layout/extra_spacing.rb +++ b/lib/rubocop/cop/layout/extra_spacing.rb @@ -49,7 +49,7 @@ def on_new_investigation private - def aligned_locations(locs) + def aligned_locations(locs) # rubocop:disable Metrics/AbcSize return [] if locs.empty? aligned = Set[locs.first.line, locs.last.line] @@ -57,6 +57,11 @@ def aligned_locations(locs) col = loc.column aligned << loc.line if col == before.column || col == after.column end + + # if locs.size > 2 and the size of variable `aligned` + # has not increased from its initial value, there are not aligned lines. + return [] if locs.size > 2 && aligned.size == 2 + aligned end diff --git a/lib/rubocop/cop/layout/first_argument_indentation.rb b/lib/rubocop/cop/layout/first_argument_indentation.rb index ef7dfbc93c6b..908aad9c3149 100644 --- a/lib/rubocop/cop/layout/first_argument_indentation.rb +++ b/lib/rubocop/cop/layout/first_argument_indentation.rb @@ -153,9 +153,10 @@ class FirstArgumentIndentation < Base MSG = 'Indent the first argument one step more than %s.' def on_send(node) + return unless should_check?(node) + return if same_line?(node, node.first_argument) return if style != :consistent && enforce_first_argument_with_fixed_indentation? && !enable_layout_first_method_argument_line_break? - return if !node.arguments? || bare_operator?(node) || node.setter_method? indent = base_indentation(node) + configured_indentation_width @@ -166,6 +167,10 @@ def on_send(node) private + def should_check?(node) + node.arguments? && !bare_operator?(node) && !node.setter_method? + end + def autocorrect(corrector, node) AlignmentCorrector.correct(corrector, processed_source, node, column_delta) end @@ -179,7 +184,7 @@ def message(arg_node) send_node = arg_node.parent text = base_range(send_node, arg_node).source.strip - base = if !/\n/.match?(text) && special_inner_call_indentation?(send_node) + base = if !text.include?("\n") && special_inner_call_indentation?(send_node) "`#{text}`" elsif comment_line?(text.lines.reverse_each.first) 'the start of the previous line (not counting the comment)' @@ -255,7 +260,7 @@ def comment_lines @comment_lines ||= processed_source .comments - .select { |c| begins_its_line?(c.loc.expression) } + .select { |c| begins_its_line?(c.source_range) } .map { |c| c.loc.line } end diff --git a/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb b/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb index c12a6ea65765..da6b07c36d95 100644 --- a/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +++ b/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb @@ -67,8 +67,7 @@ def on_send(node) outermost_send = outermost_send_on_same_line(heredoc_arg) return unless outermost_send - return unless outermost_send.loc.end - return unless heredoc_arg.first_line != outermost_send.loc.end.line + return if end_keyword_before_closing_parenthesis?(node) return if subsequent_closing_parentheses_in_same_line?(outermost_send) return if exist_argument_between_heredoc_end_and_closing_parentheses?(node) @@ -160,6 +159,12 @@ def single_line_send_with_heredoc_receiver?(node) # Closing parenthesis helpers. + def end_keyword_before_closing_parenthesis?(parenthesized_send_node) + parenthesized_send_node.ancestors.any? do |ancestor| + ancestor.loc.respond_to?(:end) && ancestor.loc.end&.source == 'end' + end + end + def subsequent_closing_parentheses_in_same_line?(outermost_send) last_arg_of_outer_send = outermost_send.last_argument return false unless last_arg_of_outer_send&.loc.respond_to?(:end) && @@ -215,6 +220,7 @@ def incorrect_parenthesis_removal_end(node) end def exist_argument_between_heredoc_end_and_closing_parentheses?(node) + return true unless node.loc.end return false unless (heredoc_end = find_most_bottom_of_heredoc_end(node.arguments)) heredoc_end < node.loc.end.begin_pos && diff --git a/lib/rubocop/cop/layout/heredoc_indentation.rb b/lib/rubocop/cop/layout/heredoc_indentation.rb index 5a7e94b35272..eb6d382fa743 100644 --- a/lib/rubocop/cop/layout/heredoc_indentation.rb +++ b/lib/rubocop/cop/layout/heredoc_indentation.rb @@ -22,6 +22,7 @@ module Layout # RUBY # class HeredocIndentation < Base + include Alignment include Heredoc extend AutoCorrector @@ -37,7 +38,7 @@ def on_heredoc(node) heredoc_indent_type = heredoc_indent_type(node) if heredoc_indent_type == '~' - expected_indent_level = base_indent_level(node) + indentation_width + expected_indent_level = base_indent_level(node) + configured_indentation_width return if expected_indent_level == body_indent_level else return unless body_indent_level.zero? @@ -66,9 +67,9 @@ def message(heredoc_indent_type) current_indent_type = "<<#{heredoc_indent_type}" if current_indent_type == '<<~' - width_message(indentation_width) + width_message(configured_indentation_width) else - type_message(indentation_width, current_indent_type) + type_message(configured_indentation_width, current_indent_type) end end @@ -89,7 +90,7 @@ def line_too_long?(node) body = heredoc_body(node) - expected_indent = base_indent_level(node) + indentation_width + expected_indent = base_indent_level(node) + configured_indentation_width actual_indent = indent_level(body) increase_indent_level = expected_indent - actual_indent @@ -114,7 +115,7 @@ def adjust_squiggly(corrector, node) end def adjust_minus(corrector, node) - heredoc_beginning = node.loc.expression.source + heredoc_beginning = node.source corrected = heredoc_beginning.sub(/<<-?/, '<<~') corrector.replace(node, corrected) end @@ -122,7 +123,7 @@ def adjust_minus(corrector, node) def indented_body(node) body = heredoc_body(node) body_indent_level = indent_level(body) - correct_indent_level = base_indent_level(node) + indentation_width + correct_indent_level = base_indent_level(node) + configured_indentation_width body.gsub(/^[^\S\r\n]{#{body_indent_level}}/, ' ' * correct_indent_level) end @@ -138,7 +139,7 @@ def indented_end(node) end def base_indent_level(node) - base_line_num = node.loc.expression.line + base_line_num = node.source_range.line base_line = processed_source.lines[base_line_num - 1] indent_level(base_line) end @@ -148,10 +149,6 @@ def heredoc_indent_type(node) node.source[/^<<([~-])/, 1] end - def indentation_width - @config.for_cop('Layout/IndentationWidth')['Width'] || 2 - end - def heredoc_body(node) node.loc.heredoc_body.source end diff --git a/lib/rubocop/cop/layout/leading_comment_space.rb b/lib/rubocop/cop/layout/leading_comment_space.rb index 5d6d614b9e85..9ea0ca0e4196 100644 --- a/lib/rubocop/cop/layout/leading_comment_space.rb +++ b/lib/rubocop/cop/layout/leading_comment_space.rb @@ -63,7 +63,7 @@ def on_new_investigation next if gemfile_ruby_comment?(comment) add_offense(comment) do |corrector| - expr = comment.loc.expression + expr = comment.source_range corrector.insert_after(hash_mark(expr), ' ') end diff --git a/lib/rubocop/cop/layout/line_continuation_leading_space.rb b/lib/rubocop/cop/layout/line_continuation_leading_space.rb index 05a0d0a4d3f7..b642087a1623 100644 --- a/lib/rubocop/cop/layout/line_continuation_leading_space.rb +++ b/lib/rubocop/cop/layout/line_continuation_leading_space.rb @@ -51,12 +51,11 @@ class LineContinuationLeadingSpace < Base private_constant :LINE_1_ENDING, :LINE_2_BEGINNING, :LEADING_STYLE_OFFENSE, :TRAILING_STYLE_OFFENSE - # rubocop:disable Metrics/AbcSize def on_dstr(node) # Quick check if we possibly have line continuations. return unless node.source.include?('\\') - end_of_first_line = node.loc.expression.begin_pos - node.loc.expression.column + end_of_first_line = node.source_range.begin_pos - node.source_range.column raw_lines(node).each_cons(2) do |raw_line_one, raw_line_two| end_of_first_line += raw_line_one.length @@ -70,7 +69,6 @@ def on_dstr(node) end end end - # rubocop:enable Metrics/AbcSize private diff --git a/lib/rubocop/cop/layout/line_continuation_spacing.rb b/lib/rubocop/cop/layout/line_continuation_spacing.rb index 1faf8ef16f70..536b4448fbcd 100644 --- a/lib/rubocop/cop/layout/line_continuation_spacing.rb +++ b/lib/rubocop/cop/layout/line_continuation_spacing.rb @@ -87,22 +87,26 @@ def autocorrect(corrector, range) corrector.replace(range, correction) end - def string_literal_ranges(ast) + # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity + def ignored_literal_ranges(ast) # which lines start inside a string literal? return [] if ast.nil? - ranges = Set.new - ast.each_node(:str, :dstr) do |str| - loc = str.location + ast.each_node(:str, :dstr, :array).with_object(Set.new) do |literal, ranges| + loc = literal.location - if str.heredoc? + if literal.array_type? + next unless literal.percent_literal? + + ranges << loc.expression + elsif literal.heredoc? ranges << loc.heredoc_body elsif loc.respond_to?(:begin) && loc.begin ranges << loc.expression end end - ranges end + # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity def comment_ranges(comments) comments.map(&:loc).map(&:expression) @@ -119,7 +123,7 @@ def ignore_range?(backtick_range) end def ignored_ranges - @ignored_ranges ||= string_literal_ranges(processed_source.ast) + + @ignored_ranges ||= ignored_literal_ranges(processed_source.ast) + comment_ranges(processed_source.comments) end diff --git a/lib/rubocop/cop/layout/redundant_line_break.rb b/lib/rubocop/cop/layout/redundant_line_break.rb index ca9d42c84623..094a51000162 100644 --- a/lib/rubocop/cop/layout/redundant_line_break.rb +++ b/lib/rubocop/cop/layout/redundant_line_break.rb @@ -50,9 +50,9 @@ class RedundantLineBreak < Base def on_send(node) # Include "the whole expression". - node = node.parent while convertible_block?(node) || - node.parent.is_a?(RuboCop::AST::BinaryOperatorNode) || - node.parent&.send_type? + node = node.parent while node.parent&.send_type? || + convertible_block?(node) || + node.parent.is_a?(RuboCop::AST::BinaryOperatorNode) return unless offense?(node) && !part_of_ignored_node?(node) @@ -69,15 +69,14 @@ def check_assignment(node, _rhs) def register_offense(node) add_offense(node) do |corrector| - corrector.replace(node.source_range, to_single_line(node.source).strip) + corrector.replace(node, to_single_line(node.source).strip) end ignore_node(node) end def offense?(node) - return false if configured_to_not_be_inspected?(node) - - node.multiline? && !too_long?(node) && suitable_as_single_line?(node) + node.multiline? && !too_long?(node) && suitable_as_single_line?(node) && + !configured_to_not_be_inspected?(node) end def configured_to_not_be_inspected?(node) diff --git a/lib/rubocop/cop/layout/rescue_ensure_alignment.rb b/lib/rubocop/cop/layout/rescue_ensure_alignment.rb index f3f0353bd9da..3b8a741c7e54 100644 --- a/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +++ b/lib/rubocop/cop/layout/rescue_ensure_alignment.rb @@ -102,7 +102,7 @@ def alignment_source(node, starting_loc) node.loc.name when :masgn mlhs_node, = *node - mlhs_node.loc.expression + mlhs_node.source_range else # It is a wrapper with access modifier. node.child_nodes.first.loc.name @@ -196,7 +196,7 @@ def alignment_location(alignment_node) if begin_end_alignment_style == 'start_of_line' start_line_range(alignment_node) else - alignment_node.loc.expression + alignment_node.source_range end end diff --git a/lib/rubocop/cop/layout/space_around_keyword.rb b/lib/rubocop/cop/layout/space_around_keyword.rb index 7c91abb2454c..90654620a939 100644 --- a/lib/rubocop/cop/layout/space_around_keyword.rb +++ b/lib/rubocop/cop/layout/space_around_keyword.rb @@ -241,7 +241,7 @@ def accept_left_square_bracket?(range) end def accept_namespace_operator?(range) - ACCEPT_NAMESPACE_OPERATOR == range.source + range.source == ACCEPT_NAMESPACE_OPERATOR end def safe_navigation_call?(range, pos) diff --git a/lib/rubocop/cop/layout/space_around_operators.rb b/lib/rubocop/cop/layout/space_around_operators.rb index 3adf1a2c55c6..207457bb48af 100644 --- a/lib/rubocop/cop/layout/space_around_operators.rb +++ b/lib/rubocop/cop/layout/space_around_operators.rb @@ -177,7 +177,7 @@ def offense(type, operator, with_space, right_operand) end def autocorrect(corrector, range) - if /\*\*/.match?(range.source) && !space_around_exponent_operator? + if range.source.include?('**') && !space_around_exponent_operator? corrector.replace(range, '**') elsif range.source.end_with?("\n") corrector.replace(range, " #{range.source.strip}\n") diff --git a/lib/rubocop/cop/layout/space_before_first_arg.rb b/lib/rubocop/cop/layout/space_before_first_arg.rb index 0430d2bc2001..303a56a624da 100644 --- a/lib/rubocop/cop/layout/space_before_first_arg.rb +++ b/lib/rubocop/cop/layout/space_before_first_arg.rb @@ -34,6 +34,7 @@ def self.autocorrect_incompatible_with def on_send(node) return unless regular_method_call_with_arguments?(node) + return if node.parenthesized? first_arg = node.first_argument.source_range first_arg_with_space = range_with_surrounding_space(first_arg, side: :left) @@ -52,7 +53,6 @@ def regular_method_call_with_arguments?(node) end def expect_params_after_method_name?(node) - return false if node.parenthesized? return true if no_space_between_method_name_and_first_argument?(node) first_arg = node.first_argument diff --git a/lib/rubocop/cop/layout/space_in_lambda_literal.rb b/lib/rubocop/cop/layout/space_in_lambda_literal.rb index d951ee820c84..d72a3eaada17 100644 --- a/lib/rubocop/cop/layout/space_in_lambda_literal.rb +++ b/lib/rubocop/cop/layout/space_in_lambda_literal.rb @@ -64,8 +64,8 @@ def space_after_arrow(lambda_node) def range_of_offense(node) range_between( - node.parent.loc.expression.begin_pos, - node.parent.arguments.loc.expression.end_pos + node.parent.source_range.begin_pos, + node.parent.arguments.source_range.end_pos ) end diff --git a/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb b/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb index c3e2488b77ed..13ddbf84f442 100644 --- a/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +++ b/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb @@ -78,8 +78,11 @@ class SpaceInsideArrayLiteralBrackets < Base def on_array(node) return unless node.square_brackets? - left, right = array_brackets(node) - return empty_offenses(node, left, right, EMPTY_MSG) if empty_brackets?(left, right) + tokens, left, right = array_brackets(node) + + if empty_brackets?(left, right, tokens: tokens) + return empty_offenses(node, left, right, EMPTY_MSG) + end start_ok = next_to_newline?(node, left) end_ok = node.single_line? ? false : end_has_own_line?(right) @@ -90,9 +93,9 @@ def on_array(node) private def autocorrect(corrector, node) - left, right = array_brackets(node) + tokens, left, right = array_brackets(node) - if empty_brackets?(left, right) + if empty_brackets?(left, right, tokens: tokens) SpaceCorrector.empty_corrections(processed_source, corrector, empty_config, left, right) elsif style == :no_space SpaceCorrector.remove_space(processed_source, corrector, left, right) @@ -104,15 +107,12 @@ def autocorrect(corrector, node) end def array_brackets(node) - [left_array_bracket(node), right_array_bracket(node)] - end + tokens = processed_source.tokens_within(node) - def left_array_bracket(node) - processed_source.tokens_within(node).find(&:left_array_bracket?) - end + left = tokens.find(&:left_array_bracket?) + right = tokens.reverse_each.find(&:right_bracket?) - def right_array_bracket(node) - processed_source.tokens_within(node).reverse.find(&:right_bracket?) + [tokens, left, right] end def empty_config @@ -178,8 +178,6 @@ def qualifies_for_compact?(node, token, side: :right) def multi_dimensional_array?(node, token, side: :right) offset = side == :right ? -1 : +1 i = index_for(node, token) + offset - # TODO: change this type check once - # https://github.com/rubocop/rubocop-ast/pull/240 is merged i += offset while processed_source.tokens_within(node)[i].new_line? if side == :right processed_source.tokens_within(node)[i].right_bracket? diff --git a/lib/rubocop/cop/layout/space_inside_block_braces.rb b/lib/rubocop/cop/layout/space_inside_block_braces.rb index 99d75d0ae671..4d8b75772ebd 100644 --- a/lib/rubocop/cop/layout/space_inside_block_braces.rb +++ b/lib/rubocop/cop/layout/space_inside_block_braces.rb @@ -146,7 +146,7 @@ def check_right_brace(node, inner, left_brace, right_brace, single_line) if single_line && /\S$/.match?(inner) no_space(right_brace.begin_pos, right_brace.end_pos, 'Space missing inside }.') else - column = node.loc.expression.column + column = node.source_range.column return if multiline_block?(left_brace, right_brace) && aligned_braces?(inner, right_brace, column) diff --git a/lib/rubocop/cop/layout/space_inside_parens.rb b/lib/rubocop/cop/layout/space_inside_parens.rb index 4f1416e4f047..caeb3c244c96 100644 --- a/lib/rubocop/cop/layout/space_inside_parens.rb +++ b/lib/rubocop/cop/layout/space_inside_parens.rb @@ -91,7 +91,7 @@ def process_with_compact_style(tokens) if !left_parens?(token1, token2) && !right_parens?(token1, token2) correct_missing_space(token1, token2) else - correct_extaneus_space_between_consecutive_parens(token1, token2) + correct_extraneous_space_between_consecutive_parens(token1, token2) end end end @@ -112,7 +112,7 @@ def correct_extraneous_space(tokens) end end - def correct_extaneus_space_between_consecutive_parens(token1, token2) + def correct_extraneous_space_between_consecutive_parens(token1, token2) return if range_between(token1.end_pos, token2.begin_pos).source != ' ' range = range_between(token1.end_pos, token2.begin_pos) diff --git a/lib/rubocop/cop/layout/space_inside_percent_literal_delimiters.rb b/lib/rubocop/cop/layout/space_inside_percent_literal_delimiters.rb index 267e00f27f42..9e2f97b847ef 100644 --- a/lib/rubocop/cop/layout/space_inside_percent_literal_delimiters.rb +++ b/lib/rubocop/cop/layout/space_inside_percent_literal_delimiters.rb @@ -83,7 +83,7 @@ def regex_matches(node, &blk) end def body_range(node) - node.location.expression.with( + node.source_range.with( begin_pos: node.location.begin.end_pos, end_pos: node.location.end.begin_pos ) diff --git a/lib/rubocop/cop/layout/space_inside_reference_brackets.rb b/lib/rubocop/cop/layout/space_inside_reference_brackets.rb index 04a0aad0ee56..f5686f438dc1 100644 --- a/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +++ b/lib/rubocop/cop/layout/space_inside_reference_brackets.rb @@ -74,7 +74,7 @@ def on_send(node) right_token = closing_bracket(tokens, left_token) - if empty_brackets?(left_token, right_token) + if empty_brackets?(left_token, right_token, tokens: tokens) return empty_offenses(node, left_token, right_token, EMPTY_MSG) end @@ -90,9 +90,9 @@ def on_send(node) private def autocorrect(corrector, node) - left, right = reference_brackets(node) + tokens, left, right = reference_brackets(node) - if empty_brackets?(left, right) + if empty_brackets?(left, right, tokens: tokens) SpaceCorrector.empty_corrections(processed_source, corrector, empty_config, left, right) elsif style == :no_space SpaceCorrector.remove_space(processed_source, corrector, left, right) @@ -104,7 +104,7 @@ def autocorrect(corrector, node) def reference_brackets(node) tokens = processed_source.tokens_within(node) left = left_ref_bracket(node, tokens) - [left, closing_bracket(tokens, left)] + [tokens, left, closing_bracket(tokens, left)] end def left_ref_bracket(node, tokens) diff --git a/lib/rubocop/cop/layout/space_inside_string_interpolation.rb b/lib/rubocop/cop/layout/space_inside_string_interpolation.rb index c7eba5b36451..e4104ec66790 100644 --- a/lib/rubocop/cop/layout/space_inside_string_interpolation.rb +++ b/lib/rubocop/cop/layout/space_inside_string_interpolation.rb @@ -31,13 +31,14 @@ class SpaceInsideStringInterpolation < Base def on_interpolation(begin_node) return if begin_node.multiline? - delims = delimiters(begin_node) - return if empty_brackets?(*delims) + tokens = processed_source.tokens_within(begin_node) + left, right = delimiters(begin_node) + return if empty_brackets?(left, right, tokens: tokens) if style == :no_space - no_space_offenses(begin_node, *delims, NO_SPACE_MSG) + no_space_offenses(begin_node, left, right, NO_SPACE_MSG) else - space_offenses(begin_node, *delims, SPACE_MSG) + space_offenses(begin_node, left, right, SPACE_MSG) end end diff --git a/lib/rubocop/cop/layout/trailing_whitespace.rb b/lib/rubocop/cop/layout/trailing_whitespace.rb index 359e3f3e317c..267976aa55fb 100644 --- a/lib/rubocop/cop/layout/trailing_whitespace.rb +++ b/lib/rubocop/cop/layout/trailing_whitespace.rb @@ -93,7 +93,7 @@ def whitespace_only?(range) end def static?(heredoc) - heredoc.loc.expression.source.end_with? "'" + heredoc.source.end_with? "'" end def skip_heredoc? diff --git a/lib/rubocop/cop/lint/ambiguous_operator.rb b/lib/rubocop/cop/lint/ambiguous_operator.rb index 4df7ba4d9423..2d656a17baa1 100644 --- a/lib/rubocop/cop/lint/ambiguous_operator.rb +++ b/lib/rubocop/cop/lint/ambiguous_operator.rb @@ -38,6 +38,10 @@ class AmbiguousOperator < Base 'a whitespace to the right of the `%s` if it ' \ 'should be %s.' + def self.autocorrect_incompatible_with + [Naming::BlockForwarding] + end + def on_new_investigation processed_source.diagnostics.each do |diagnostic| next unless diagnostic.reason == :ambiguous_prefix diff --git a/lib/rubocop/cop/lint/constant_resolution.rb b/lib/rubocop/cop/lint/constant_resolution.rb index f03666c57145..c09deaa440bd 100644 --- a/lib/rubocop/cop/lint/constant_resolution.rb +++ b/lib/rubocop/cop/lint/constant_resolution.rb @@ -68,7 +68,7 @@ class ConstantResolution < Base PATTERN def on_const(node) - return if !unqualified_const?(node) || node.parent&.defined_module + return if !unqualified_const?(node) || node.parent&.defined_module || node.loc.nil? add_offense(node) end diff --git a/lib/rubocop/cop/lint/debugger.rb b/lib/rubocop/cop/lint/debugger.rb index 2f1d2ed0b30a..6cf1a13f2a33 100644 --- a/lib/rubocop/cop/lint/debugger.rb +++ b/lib/rubocop/cop/lint/debugger.rb @@ -70,6 +70,9 @@ class Debugger < Base def on_send(node) return unless debugger_method?(node) + # Basically, debugger methods are not used as a method argument without arguments. + return if node.arguments.empty? && node.each_ancestor(:send, :csend).any? + add_offense(node) end @@ -82,44 +85,25 @@ def message(node) def debugger_methods @debugger_methods ||= begin config = cop_config.fetch('DebuggerMethods', []) - values = config.is_a?(Array) ? config : config.values.flatten - values.map do |v| - next unless v - - *receiver, method_name = v.split('.') - { - receiver: receiver.empty? ? nil : receiver.map(&:to_sym), - method_name: method_name.to_sym - } - end.compact + config.is_a?(Array) ? config : config.values.flatten end end def debugger_method?(send_node) - method_name = send_node.method_name - - debugger_methods.any? do |method| - next unless method[:method_name] == method_name + return if send_node.parent&.send_type? && send_node.parent.receiver == send_node - if method[:receiver].nil? - send_node.receiver.nil? - else - method[:receiver] == receiver_chain(send_node) - end - end + debugger_methods.include?(chained_method_name(send_node)) end - def receiver_chain(send_node) - receivers = [] + def chained_method_name(send_node) + chained_method_name = send_node.method_name.to_s receiver = send_node.receiver - while receiver - name = receiver.send_type? ? receiver.method_name : receiver.const_name&.to_sym - receivers.unshift(name) + name = receiver.send_type? ? receiver.method_name : receiver.const_name + chained_method_name = "#{name}.#{chained_method_name}" receiver = receiver.receiver end - - receivers + chained_method_name end end end diff --git a/lib/rubocop/cop/lint/deprecated_class_methods.rb b/lib/rubocop/cop/lint/deprecated_class_methods.rb index e1270e9c5ef4..2790c321e6e4 100644 --- a/lib/rubocop/cop/lint/deprecated_class_methods.rb +++ b/lib/rubocop/cop/lint/deprecated_class_methods.rb @@ -11,6 +11,8 @@ module Lint # File.exists?(some_path) # Dir.exists?(some_path) # iterator? + # attr :name, true + # attr :name, false # ENV.freeze # Calling `Env.freeze` raises `TypeError` since Ruby 2.7. # ENV.clone # ENV.dup # Calling `Env.dup` raises `TypeError` since Ruby 3.1. @@ -21,6 +23,8 @@ module Lint # File.exist?(some_path) # Dir.exist?(some_path) # block_given? + # attr_accessor :name + # attr_reader :name # ENV # `ENV.freeze` cannot prohibit changes to environment variables. # ENV.to_h # ENV.to_h # `ENV.dup` cannot dup `ENV`, use `ENV.to_h` to get a copy of `ENV` as a hash. @@ -29,138 +33,84 @@ module Lint class DeprecatedClassMethods < Base extend AutoCorrector - # Inner class to DeprecatedClassMethods. - # This class exists to add abstraction and clean naming - # to the deprecated objects - class DeprecatedClassMethod - include RuboCop::AST::Sexp + MSG = '`%s` is deprecated in favor of `%s`.' + RESTRICT_ON_SEND = %i[ + attr clone dup exists? freeze gethostbyaddr gethostbyname iterator? + ].freeze + + PREFERRED_METHODS = { + clone: 'to_h', + dup: 'to_h', + exists?: 'exist?', + gethostbyaddr: 'Addrinfo#getnameinfo', + gethostbyname: 'Addrinfo#getaddrinfo', + iterator?: 'block_given?' + }.freeze - attr_reader :method, :class_constant + DIR_ENV_FILE_CONSTANTS = %i[Dir ENV File].freeze - def initialize(method, class_constant: nil, correctable: true) - @method = method - @class_constant = class_constant - @correctable = correctable - end - - def class_nodes - @class_nodes ||= - if class_constant - [ - s(:const, nil, class_constant), - s(:const, s(:cbase), class_constant) - ] - else - [nil] - end - end + # @!method deprecated_class_method?(node) + def_node_matcher :deprecated_class_method?, <<~PATTERN + { + (send (const {cbase nil?} {:ENV}) {:clone :dup :freeze}) + (send (const {cbase nil?} {:File :Dir}) :exists? _) + (send (const {cbase nil?} :Socket) {:gethostbyaddr :gethostbyname} ...) + (send nil? :attr _ boolean) + (send nil? :iterator?) + } + PATTERN - def correctable? - @correctable - end + def on_send(node) + return unless deprecated_class_method?(node) - def to_s - [class_constant, method].compact.join(delimiter) - end + offense_range = offense_range(node) + prefer = preferred_method(node) + message = format(MSG, current: offense_range.source, prefer: prefer) - private + add_offense(offense_range, message: message) do |corrector| + next if socket_const?(node.receiver) - def delimiter - CLASS_METHOD_DELIMITER + if node.method?(:freeze) + corrector.replace(node, 'ENV') + else + corrector.replace(offense_range, prefer) + end end end - # Inner class to DeprecatedClassMethods. - # This class exists to add abstraction and clean naming - # to the replacements for deprecated objects - class Replacement - attr_reader :method, :class_constant - - def initialize(method, class_constant: nil, instance_method: false) - @method = method - @class_constant = class_constant - @instance_method = instance_method - end - - def to_s - [class_constant, method].compact.join(delimiter) - end - - private - - def delimiter - instance_method? ? INSTANCE_METHOD_DELIMITER : CLASS_METHOD_DELIMITER - end + private - def instance_method? - @instance_method + def offense_range(node) + if socket_const?(node.receiver) || dir_env_file_const?(node.receiver) + node.source_range.begin.join(node.loc.selector.end) + elsif node.method?(:attr) + node + else + node.loc.selector end end - MSG = '`%s` is deprecated in favor of `%s`.' - - DEPRECATED_METHODS_OBJECT = { - DeprecatedClassMethod.new(:exists?, class_constant: :File) => - Replacement.new(:exist?, class_constant: :File), - - DeprecatedClassMethod.new(:exists?, class_constant: :Dir) => - Replacement.new(:exist?, class_constant: :Dir), - - DeprecatedClassMethod.new(:iterator?) => Replacement.new(:block_given?), - - DeprecatedClassMethod.new(:freeze, class_constant: :ENV) => - Replacement.new(nil, class_constant: :ENV), + def preferred_method(node) + if node.method?(:attr) + boolean_argument = node.arguments[1].source + preferred_attr_method = boolean_argument == 'true' ? 'attr_accessor' : 'attr_reader' - DeprecatedClassMethod.new(:clone, class_constant: :ENV) => - Replacement.new(:to_h, class_constant: :ENV), - - DeprecatedClassMethod.new(:dup, class_constant: :ENV) => - Replacement.new(:to_h, class_constant: :ENV), - - DeprecatedClassMethod.new(:gethostbyaddr, class_constant: :Socket, correctable: false) => - Replacement.new(:getnameinfo, class_constant: :Addrinfo, instance_method: true), - - DeprecatedClassMethod.new(:gethostbyname, class_constant: :Socket, correctable: false) => - Replacement.new(:getaddrinfo, class_constant: :Addrinfo, instance_method: true) - }.freeze + "#{preferred_attr_method} #{node.first_argument.source}" + elsif dir_env_file_const?(node.receiver) + prefer = PREFERRED_METHODS[node.method_name] - RESTRICT_ON_SEND = DEPRECATED_METHODS_OBJECT.keys.map(&:method).freeze - - CLASS_METHOD_DELIMITER = '.' - INSTANCE_METHOD_DELIMITER = '#' - - def on_send(node) - check(node) do |deprecated| - prefer = replacement(deprecated) - message = format(MSG, current: deprecated, prefer: prefer) - current_method = node.loc.selector - - add_offense(current_method, message: message) do |corrector| - next unless deprecated.correctable? - - if (preferred_method = prefer.method) - corrector.replace(current_method, preferred_method) - else - corrector.remove(node.loc.dot) - corrector.remove(current_method) - end - end + prefer ? "#{node.receiver.source}.#{prefer}" : 'ENV' + else + PREFERRED_METHODS[node.method_name] end end - private - - def check(node) - DEPRECATED_METHODS_OBJECT.each_key do |deprecated| - next unless deprecated.class_nodes.include?(node.receiver) - next unless node.method?(deprecated.method) - - yield deprecated - end + def socket_const?(node) + node&.short_name == :Socket end - def replacement(deprecated) - DEPRECATED_METHODS_OBJECT[deprecated] + def dir_env_file_const?(node) + DIR_ENV_FILE_CONSTANTS.include?(node&.short_name) end end end diff --git a/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb b/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb index 13a065c24ad5..c37806cf29de 100644 --- a/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +++ b/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb @@ -100,7 +100,7 @@ def message(node) end def correction_range(node) - range_between(node.loc.dot.end_pos, node.loc.expression.end_pos) + range_between(node.loc.dot.end_pos, node.source_range.end_pos) end def openssl_class(node) diff --git a/lib/rubocop/cop/lint/duplicate_methods.rb b/lib/rubocop/cop/lint/duplicate_methods.rb index 7df0b9362e41..12c4dd6003a5 100644 --- a/lib/rubocop/cop/lint/duplicate_methods.rb +++ b/lib/rubocop/cop/lint/duplicate_methods.rb @@ -187,7 +187,7 @@ def location(node) if DEF_TYPES.include?(node.type) node.loc.keyword.join(node.loc.name) else - node.loc.expression + node.source_range end end @@ -258,7 +258,7 @@ def possible_dsl?(node) end def source_location(node) - range = node.location.expression + range = node.source_range path = smart_path(range.source_buffer.name) "#{path}:#{range.line}" end diff --git a/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb b/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb index e30c38139343..c4f82a713599 100644 --- a/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +++ b/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb @@ -86,9 +86,7 @@ def interpolation_locs(node) # Cache by loc, not by regexp content, as content can be repeated in multiple patterns key = node.loc - @interpolation_locs[key] ||= node.children.select(&:begin_type?).map do |interpolation| - interpolation.loc.expression - end + @interpolation_locs[key] ||= node.children.select(&:begin_type?).map(&:source_range) end end end diff --git a/lib/rubocop/cop/lint/else_layout.rb b/lib/rubocop/cop/lint/else_layout.rb index f728b5c2ed33..ba0e586ffe24 100644 --- a/lib/rubocop/cop/lint/else_layout.rb +++ b/lib/rubocop/cop/lint/else_layout.rb @@ -41,6 +41,7 @@ module Lint # do_that # end class ElseLayout < Base + include Alignment include RangeHelp extend AutoCorrector @@ -80,13 +81,8 @@ def check_else(node) def autocorrect(corrector, node, first_else) corrector.insert_after(node.loc.else, "\n") - blank_range = range_between(node.loc.else.end_pos, first_else.loc.expression.begin_pos) - indentation = indent(node, offset: indentation_width) - corrector.replace(blank_range, indentation) - end - - def indentation_width - @config.for_cop('Layout/IndentationWidth')['Width'] || 2 + blank_range = range_between(node.loc.else.end_pos, first_else.source_range.begin_pos) + corrector.replace(blank_range, indentation(node)) end end end diff --git a/lib/rubocop/cop/lint/empty_block.rb b/lib/rubocop/cop/lint/empty_block.rb index 2d3058da4942..b401efd5d02a 100644 --- a/lib/rubocop/cop/lint/empty_block.rb +++ b/lib/rubocop/cop/lint/empty_block.rb @@ -77,7 +77,7 @@ def allow_comment?(node) return false unless processed_source.contains_comment?(node.source_range) line_comment = processed_source.comment_at_line(node.source_range.line) - !line_comment || !comment_disables_cop?(line_comment.loc.expression.source) + !line_comment || !comment_disables_cop?(line_comment.source) end def allow_empty_lambdas? diff --git a/lib/rubocop/cop/lint/empty_conditional_body.rb b/lib/rubocop/cop/lint/empty_conditional_body.rb index ae2199dd65a1..97a7ca078576 100644 --- a/lib/rubocop/cop/lint/empty_conditional_body.rb +++ b/lib/rubocop/cop/lint/empty_conditional_body.rb @@ -72,6 +72,8 @@ def on_if(node) return if cop_config['AllowComments'] && contains_comments?(node) add_offense(node, message: format(MSG, keyword: node.keyword)) do |corrector| + next if node.parent&.call_type? + autocorrect(corrector, node) end end @@ -86,7 +88,7 @@ def autocorrect(corrector, node) def remove_comments(corrector, node) comments_in_range(node).each do |comment| - range = range_by_whole_lines(comment.loc.expression, include_final_newline: true) + range = range_by_whole_lines(comment.source_range, include_final_newline: true) corrector.remove(range) end end @@ -141,7 +143,7 @@ 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.loc.expression.end_pos) + 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.with(end_pos: if_node.loc.end.end_pos) diff --git a/lib/rubocop/cop/lint/empty_interpolation.rb b/lib/rubocop/cop/lint/empty_interpolation.rb index 8e1b40bce658..b5be737b5c08 100644 --- a/lib/rubocop/cop/lint/empty_interpolation.rb +++ b/lib/rubocop/cop/lint/empty_interpolation.rb @@ -25,7 +25,7 @@ class EmptyInterpolation < Base def on_interpolation(begin_node) return unless begin_node.children.empty? - add_offense(begin_node) { |corrector| corrector.remove(begin_node.loc.expression) } + add_offense(begin_node) { |corrector| corrector.remove(begin_node) } end end end diff --git a/lib/rubocop/cop/lint/format_parameter_mismatch.rb b/lib/rubocop/cop/lint/format_parameter_mismatch.rb index defa0362a886..2de03ce86993 100644 --- a/lib/rubocop/cop/lint/format_parameter_mismatch.rb +++ b/lib/rubocop/cop/lint/format_parameter_mismatch.rb @@ -81,6 +81,10 @@ def offending_node?(node) return false if num_of_format_args == :unknown + first_arg = node.first_argument + return false if num_of_expected_fields.zero? && + (first_arg.dstr_type? || first_arg.array_type?) + matched_arguments_count?(num_of_expected_fields, num_of_format_args) end @@ -94,8 +98,8 @@ def matched_arguments_count?(expected, passed) # @!method called_on_string?(node) def_node_matcher :called_on_string?, <<~PATTERN - {(send {nil? const_type?} _ (str _) ...) - (send (str ...) ...)} + {(send {nil? const_type?} _ {str dstr} ...) + (send {str dstr} ...)} PATTERN def method_with_format_args?(node) @@ -143,11 +147,11 @@ def format_method?(name, node) return false if node.const_receiver? && !node.receiver.loc.name.is?(KERNEL) return false unless node.method?(name) - node.arguments.size > 1 && node.first_argument.str_type? + node.arguments.size > 1 && string_type?(node.first_argument) end def expected_fields_count(node) - return :unknown unless node.str_type? + return :unknown unless string_type?(node) format_string = RuboCop::Cop::Utils::FormatString.new(node.source) return 1 if format_string.named_interpolation? @@ -172,10 +176,9 @@ def sprintf?(node) def percent?(node) receiver = node.receiver - percent = node.method?(:%) && - (STRING_TYPES.include?(receiver.type) || node.first_argument.array_type?) + percent = node.method?(:%) && (string_type?(receiver) || node.first_argument.array_type?) - return false if percent && STRING_TYPES.include?(receiver.type) && heredoc?(node) + return false if percent && string_type?(receiver) && heredoc?(node) percent end @@ -188,6 +191,10 @@ def message(node) format(MSG, arg_num: num_args_for_format, method: method_name, field_num: num_expected_fields) end + + def string_type?(node) + STRING_TYPES.include?(node.type) + end end end end diff --git a/lib/rubocop/cop/lint/heredoc_method_call_position.rb b/lib/rubocop/cop/lint/heredoc_method_call_position.rb index 0e4edebb9096..0c3c5a2b3074 100644 --- a/lib/rubocop/cop/lint/heredoc_method_call_position.rb +++ b/lib/rubocop/cop/lint/heredoc_method_call_position.rb @@ -8,27 +8,25 @@ module Lint # # @example # # bad + # <<-SQL + # bar + # SQL + # .strip_indent # - # <<-SQL - # bar - # SQL - # .strip_indent - # - # <<-SQL - # bar - # SQL - # .strip_indent - # .trim + # <<-SQL + # bar + # SQL + # .strip_indent + # .trim # # # good + # <<~SQL + # bar + # SQL # - # <<~SQL - # bar - # SQL - # - # <<~SQL.trim - # bar - # SQL + # <<~SQL.trim + # bar + # SQL # class HeredocMethodCallPosition < Base include RangeHelp diff --git a/lib/rubocop/cop/lint/implicit_string_concatenation.rb b/lib/rubocop/cop/lint/implicit_string_concatenation.rb index baacb71dbef5..b8eb5af21860 100644 --- a/lib/rubocop/cop/lint/implicit_string_concatenation.rb +++ b/lib/rubocop/cop/lint/implicit_string_concatenation.rb @@ -81,7 +81,7 @@ def string_literals?(node1, node2) end def display_str(node) - if /\n/.match?(node.source) + if node.source.include?("\n") str_content(node).inspect else node.source diff --git a/lib/rubocop/cop/lint/ineffective_access_modifier.rb b/lib/rubocop/cop/lint/ineffective_access_modifier.rb index a6984ad7bea9..b918933f237c 100644 --- a/lib/rubocop/cop/lint/ineffective_access_modifier.rb +++ b/lib/rubocop/cop/lint/ineffective_access_modifier.rb @@ -83,7 +83,7 @@ def format_message(modifier) ALTERNATIVE_PROTECTED end format(MSG, modifier: visibility, - line: modifier.location.expression.line, + line: modifier.source_range.line, alternative: alternative) end diff --git a/lib/rubocop/cop/lint/literal_in_interpolation.rb b/lib/rubocop/cop/lint/literal_in_interpolation.rb index 638258c26124..ab2e2539d066 100644 --- a/lib/rubocop/cop/lint/literal_in_interpolation.rb +++ b/lib/rubocop/cop/lint/literal_in_interpolation.rb @@ -58,7 +58,7 @@ def special_keyword?(node) (node.str_type? && !node.loc.respond_to?(:begin)) || node.source_range.is?('__LINE__') end - # rubocop:disable Metrics/MethodLength + # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity def autocorrected_value(node) case node.type when :int @@ -71,13 +71,15 @@ def autocorrected_value(node) autocorrected_value_for_symbol(node) when :array autocorrected_value_for_array(node) + when :hash + autocorrected_value_for_hash(node) when :nil '' else node.source.gsub('"', '\"') end end - # rubocop:enable Metrics/MethodLength + # rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity def autocorrected_value_for_string(node) if node.source.start_with?("'", '%q') @@ -89,9 +91,18 @@ def autocorrected_value_for_string(node) def autocorrected_value_for_symbol(node) end_pos = - node.loc.end ? node.loc.end.begin_pos : node.loc.expression.end_pos + node.loc.end ? node.loc.end.begin_pos : node.source_range.end_pos - range_between(node.loc.begin.end_pos, end_pos).source + range_between(node.loc.begin.end_pos, end_pos).source.gsub('"', '\"') + end + + def autocorrected_value_in_hash_for_symbol(node) + # TODO: We need to detect symbol unacceptable names more correctly + if / |"|'/.match?(node.value.to_s) + ":\\\"#{node.value.to_s.gsub('"') { '\\\\\"' }}\\\"" + else + ":#{node.value}" + end end def autocorrected_value_for_array(node) @@ -100,6 +111,37 @@ def autocorrected_value_for_array(node) contents_range(node).source.split.to_s.gsub('"', '\"') end + def autocorrected_value_for_hash(node) + hash_string = node.children.map do |child| + key = autocorrected_value_in_hash(child.key) + value = autocorrected_value_in_hash(child.value) + "#{key}=>#{value}" + end.join(', ') + + "{#{hash_string}}" + end + + # rubocop:disable Metrics/MethodLength, Metrics/AbcSize + def autocorrected_value_in_hash(node) + case node.type + when :int + node.children.last.to_i.to_s + when :float + node.children.last.to_f.to_s + when :str + "\\\"#{node.value.to_s.gsub('"') { '\\\\\"' }}\\\"" + when :sym + autocorrected_value_in_hash_for_symbol(node) + when :array + autocorrected_value_for_array(node) + when :hash + autocorrected_value_for_hash(node) + else + node.source.gsub('"', '\"') + end + end + # rubocop:enable Metrics/MethodLength, Metrics/AbcSize + # Does node print its own source when converted to a string? def prints_as_self?(node) node.basic_literal? || diff --git a/lib/rubocop/cop/lint/missing_super.rb b/lib/rubocop/cop/lint/missing_super.rb index 9236890ec347..97abee878f87 100644 --- a/lib/rubocop/cop/lint/missing_super.rb +++ b/lib/rubocop/cop/lint/missing_super.rb @@ -28,6 +28,21 @@ module Lint # end # # # bad + # Employee = Class.new(Person) do + # def initialize(name, salary) + # @salary = salary + # end + # end + # + # # good + # Employee = Class.new(Person) do + # def initialize(name, salary) + # super(name) + # @salary = salary + # end + # end + # + # # bad # class Parent # def self.inherited(base) # do_something @@ -55,6 +70,13 @@ class MissingSuper < Base CALLBACKS = (CLASS_LIFECYCLE_CALLBACKS + METHOD_LIFECYCLE_CALLBACKS).to_set.freeze + # @!method class_new_block(node) + def_node_matcher :class_new_block, <<~RUBY + ({block numblock} + (send + (const {nil? cbase} :Class) :new $_) ...) + RUBY + def on_def(node) return unless offender?(node) @@ -88,8 +110,15 @@ def contains_super?(node) end def inside_class_with_stateful_parent?(node) - class_node = node.each_ancestor(:class).first - class_node&.parent_class && !stateless_class?(class_node.parent_class) + if (block_node = node.each_ancestor(:block, :numblock).first) + return false unless (super_class = class_new_block(block_node)) + + !stateless_class?(super_class) + elsif (class_node = node.each_ancestor(:class).first) + class_node.parent_class && !stateless_class?(class_node.parent_class) + else + false + end end def stateless_class?(node) diff --git a/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb b/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb index 86a3391a0a28..7274dcbff4d2 100644 --- a/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb +++ b/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb @@ -8,6 +8,7 @@ module Lint # Replace numbered captures with non-capturing groupings or # named captures. # + # @example # # bad # /(?FOO)(BAR)/ # diff --git a/lib/rubocop/cop/lint/nested_method_definition.rb b/lib/rubocop/cop/lint/nested_method_definition.rb index 6c9ed0fe0a99..36fa822e5adf 100644 --- a/lib/rubocop/cop/lint/nested_method_definition.rb +++ b/lib/rubocop/cop/lint/nested_method_definition.rb @@ -108,7 +108,7 @@ def on_def(node) return unless def_ancestor within_scoping_def = - node.each_ancestor(:block, :sclass).any? do |ancestor| + node.each_ancestor(:block, :numblock, :sclass).any? do |ancestor| scoping_method_call?(ancestor) end @@ -120,7 +120,7 @@ def on_def(node) def scoping_method_call?(child) child.sclass_type? || eval_call?(child) || exec_call?(child) || - class_or_module_or_struct_new_call?(child) || allowed_method_name?(child) + child.class_constructor? || allowed_method_name?(child) end def allowed_method_name?(node) @@ -131,17 +131,12 @@ def allowed_method_name?(node) # @!method eval_call?(node) def_node_matcher :eval_call?, <<~PATTERN - (block (send _ {:instance_eval :class_eval :module_eval} ...) ...) + ({block numblock} (send _ {:instance_eval :class_eval :module_eval} ...) ...) PATTERN # @!method exec_call?(node) def_node_matcher :exec_call?, <<~PATTERN - (block (send _ {:instance_exec :class_exec :module_exec} ...) ...) - PATTERN - - # @!method class_or_module_or_struct_new_call?(node) - def_node_matcher :class_or_module_or_struct_new_call?, <<~PATTERN - (block (send (const {nil? cbase} {:Class :Module :Struct}) :new ...) ...) + ({block numblock} (send _ {:instance_exec :class_exec :module_exec} ...) ...) PATTERN end end diff --git a/lib/rubocop/cop/lint/or_assignment_to_constant.rb b/lib/rubocop/cop/lint/or_assignment_to_constant.rb index 284d91e61208..4b366285e4ea 100644 --- a/lib/rubocop/cop/lint/or_assignment_to_constant.rb +++ b/lib/rubocop/cop/lint/or_assignment_to_constant.rb @@ -31,6 +31,8 @@ def on_or_asgn(node) return unless lhs&.casgn_type? add_offense(node.loc.operator) do |corrector| + next if node.each_ancestor(:def, :defs).any? + corrector.replace(node.loc.operator, '=') end end diff --git a/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb b/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb index ed11778cd9f2..0d62f90653d1 100644 --- a/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +++ b/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb @@ -69,7 +69,7 @@ def on_when(node) end def on_in_pattern(node) - regexp_patterns = patterns(node).select(&:regexp_type?) + regexp_patterns = regexp_patterns(node) @valid_ref = regexp_patterns.map { |pattern| check_regexp(pattern) }.compact.max end @@ -90,16 +90,12 @@ def on_nth_ref(node) private - def patterns(pattern_node) - pattern = pattern_node.node_parts[0] - - case pattern.type - when :array_pattern, :match_alt - pattern.children - when :match_as - patterns(pattern) - else + def regexp_patterns(in_node) + pattern = in_node.pattern + if pattern.regexp_type? [pattern] + else + pattern.each_descendant(:regexp).to_a end end diff --git a/lib/rubocop/cop/lint/percent_string_array.rb b/lib/rubocop/cop/lint/percent_string_array.rb index a4233b5679e3..da98438531e0 100644 --- a/lib/rubocop/cop/lint/percent_string_array.rb +++ b/lib/rubocop/cop/lint/percent_string_array.rb @@ -50,7 +50,7 @@ def on_percent_literal(node) add_offense(node) do |corrector| node.each_value do |value| - range = value.loc.expression + range = value.source_range match = range.source.match(TRAILING_QUOTE) corrector.remove_trailing(range, match[0].length) if match diff --git a/lib/rubocop/cop/lint/percent_symbol_array.rb b/lib/rubocop/cop/lint/percent_symbol_array.rb index dcf7c0e1175e..89e2cf2b32b5 100644 --- a/lib/rubocop/cop/lint/percent_symbol_array.rb +++ b/lib/rubocop/cop/lint/percent_symbol_array.rb @@ -41,7 +41,7 @@ def on_percent_literal(node) def autocorrect(corrector, node) node.children.each do |child| - range = child.loc.expression + range = child.source_range corrector.remove_trailing(range, 1) if range.source.end_with?(',') corrector.remove_leading(range, 1) if diff --git a/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb b/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb index 9b6de1cd542a..d1f66ece0f3e 100644 --- a/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +++ b/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb @@ -231,7 +231,7 @@ def add_offense_for_entire_comment(comment, cops) cop_names = cops.sort.map { |c| describe(c) }.join(', ') add_offense(location, message: message(cop_names)) do |corrector| - range = comment_range_with_surrounding_space(location, comment.loc.expression) + range = comment_range_with_surrounding_space(location, comment.source_range) if leave_free_comment?(comment, range) corrector.replace(range, ' # ') @@ -263,8 +263,8 @@ def leave_free_comment?(comment, range) def cop_range(comment, cop) cop = remove_department_marker(cop) - matching_range(comment.loc.expression, cop) || - matching_range(comment.loc.expression, Badge.parse(cop).cop_name) || + matching_range(comment.source_range, cop) || + matching_range(comment.source_range, Badge.parse(cop).cop_name) || raise("Couldn't find #{cop} in comment: #{comment.text}") end @@ -281,15 +281,21 @@ def trailing_range?(ranges, range) .drop_while { |r| !r.equal?(range) } .each_cons(2) .map { |range1, range2| range1.end.join(range2.begin).source } - .all? { |intervening| /\A\s*,\s*\Z/.match?(intervening) } + .all?(/\A\s*,\s*\z/) end + SIMILAR_COP_NAMES_CACHE = Hash.new do |hash, cop_name| + hash[:all_cop_names] = Registry.global.names unless hash.key?(:all_cop_names) + hash[cop_name] = NameSimilarity.find_similar_name(cop_name, hash[:all_cop_names]) + end + private_constant :SIMILAR_COP_NAMES_CACHE + def describe(cop) return 'all cops' if cop == 'all' return "`#{remove_department_marker(cop)}` department" if department_marker?(cop) return "`#{cop}`" if all_cop_names.include?(cop) - similar = NameSimilarity.find_similar_name(cop, all_cop_names) + similar = SIMILAR_COP_NAMES_CACHE[cop] similar ? "`#{cop}` (did you mean `#{similar}`?)" : "`#{cop}` (unknown cop)" end diff --git a/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb b/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb index 4d1fd8654f66..99e76b0381f5 100644 --- a/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +++ b/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb @@ -74,7 +74,7 @@ def range_of_offense(comment, name) end def comment_start(comment) - comment.loc.expression.begin_pos + comment.source_range.begin_pos end def cop_name_indention(comment, name) @@ -82,7 +82,7 @@ def cop_name_indention(comment, name) end def range_with_comma(comment, name) - source = comment.loc.expression.source + source = comment.source begin_pos = cop_name_indention(comment, name) end_pos = begin_pos + name.size @@ -94,14 +94,14 @@ def range_with_comma(comment, name) def range_to_remove(begin_pos, end_pos, comment) start = comment_start(comment) - source = comment.loc.expression.source + source = comment.source if source[begin_pos - 1] == ',' range_with_comma_before(start, begin_pos, end_pos) elsif source[end_pos] == ',' range_with_comma_after(comment, start, begin_pos, end_pos) else - range_between(start, comment.loc.expression.end_pos) + range_between(start, comment.source_range.end_pos) end end @@ -112,7 +112,7 @@ def range_with_comma_before(start, begin_pos, end_pos) # If the list of cops is comma-separated, but without a empty space after the comma, # we should **not** remove the prepending empty space, thus begin_pos += 1 def range_with_comma_after(comment, start, begin_pos, end_pos) - begin_pos += 1 if comment.loc.expression.source[end_pos + 1] != ' ' + begin_pos += 1 if comment.source[end_pos + 1] != ' ' range_between(start + begin_pos, start + end_pos + 1) end diff --git a/lib/rubocop/cop/lint/redundant_require_statement.rb b/lib/rubocop/cop/lint/redundant_require_statement.rb index 3ed6aff96701..1777024059b2 100644 --- a/lib/rubocop/cop/lint/redundant_require_statement.rb +++ b/lib/rubocop/cop/lint/redundant_require_statement.rb @@ -38,6 +38,10 @@ class RedundantRequireStatement < Base MSG = 'Remove unnecessary `require` statement.' RESTRICT_ON_SEND = %i[require].freeze RUBY_22_LOADED_FEATURES = %w[rational complex].freeze + PRETTY_PRINT_METHODS = %i[ + pretty_inspect pretty_print pretty_print_cycle + pretty_print_inspect pretty_print_instance_variables + ].freeze # @!method redundant_require_statement?(node) def_node_matcher :redundant_require_statement?, <<~PATTERN @@ -52,7 +56,7 @@ def on_send(node) if node.parent.respond_to?(:modifier_form?) && node.parent.modifier_form? corrector.insert_after(node.parent, "\nend") - range = range_with_surrounding_space(node.loc.expression, side: :right) + range = range_with_surrounding_space(node.source_range, side: :right) else range = range_by_whole_lines(node.source_range, include_final_newline: true) end @@ -68,12 +72,18 @@ def redundant_feature?(feature_name) feature_name == 'enumerator' || (target_ruby_version >= 2.1 && feature_name == 'thread') || (target_ruby_version >= 2.2 && RUBY_22_LOADED_FEATURES.include?(feature_name)) || - (target_ruby_version >= 2.5 && feature_name == 'pp') || + (target_ruby_version >= 2.5 && feature_name == 'pp' && !use_pretty_print_method?) || (target_ruby_version >= 2.7 && feature_name == 'ruby2_keywords') || (target_ruby_version >= 3.1 && feature_name == 'fiber') || (target_ruby_version >= 3.2 && feature_name == 'set') end # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity + + def use_pretty_print_method? + processed_source.ast.each_descendant(:send).any? do |node| + PRETTY_PRINT_METHODS.include?(node.method_name) + end + end end end end diff --git a/lib/rubocop/cop/lint/redundant_splat_expansion.rb b/lib/rubocop/cop/lint/redundant_splat_expansion.rb index 1465b749fe30..940f404f772f 100644 --- a/lib/rubocop/cop/lint/redundant_splat_expansion.rb +++ b/lib/rubocop/cop/lint/redundant_splat_expansion.rb @@ -141,7 +141,7 @@ def replacement_range_and_content(node) expression = loc.expression if array_new?(variable) - expression = node.parent.loc.expression if node.parent.array_type? + expression = node.parent.source_range if node.parent.array_type? [expression, variable.source] elsif !variable.array_type? [expression, "[#{variable.source}]"] diff --git a/lib/rubocop/cop/lint/redundant_with_index.rb b/lib/rubocop/cop/lint/redundant_with_index.rb index 8b22899f5cf7..229963dba420 100644 --- a/lib/rubocop/cop/lint/redundant_with_index.rb +++ b/lib/rubocop/cop/lint/redundant_with_index.rb @@ -72,7 +72,7 @@ def message(node) end def with_index_range(send) - range_between(send.loc.selector.begin_pos, send.loc.expression.end_pos) + range_between(send.loc.selector.begin_pos, send.source_range.end_pos) end end end diff --git a/lib/rubocop/cop/lint/redundant_with_object.rb b/lib/rubocop/cop/lint/redundant_with_object.rb index 3e3c0b24ac67..39aa24e0d7bd 100644 --- a/lib/rubocop/cop/lint/redundant_with_object.rb +++ b/lib/rubocop/cop/lint/redundant_with_object.rb @@ -71,7 +71,7 @@ def message(node) end def with_object_range(send) - range_between(send.loc.selector.begin_pos, send.loc.expression.end_pos) + range_between(send.loc.selector.begin_pos, send.source_range.end_pos) end end end diff --git a/lib/rubocop/cop/lint/refinement_import_methods.rb b/lib/rubocop/cop/lint/refinement_import_methods.rb index 94b3aad8e342..3c1de3a4240e 100644 --- a/lib/rubocop/cop/lint/refinement_import_methods.rb +++ b/lib/rubocop/cop/lint/refinement_import_methods.rb @@ -41,7 +41,8 @@ class RefinementImportMethods < Base def on_send(node) return if node.receiver - return unless node.parent.block_type? && node.parent.method?(:refine) + return unless (parent = node.parent) + return unless parent.block_type? && parent.method?(:refine) add_offense(node.loc.selector, message: format(MSG, current: node.method_name)) end diff --git a/lib/rubocop/cop/lint/rescue_type.rb b/lib/rubocop/cop/lint/rescue_type.rb index 9d7d0ced0709..0fed979a607a 100644 --- a/lib/rubocop/cop/lint/rescue_type.rb +++ b/lib/rubocop/cop/lint/rescue_type.rb @@ -50,7 +50,7 @@ def on_resbody(node) return if invalid_exceptions.empty? add_offense( - node.loc.keyword.join(rescued.loc.expression), + node.loc.keyword.join(rescued.source_range), message: format(MSG, invalid_exceptions: invalid_exceptions.map(&:source).join(', ')) ) do |corrector| autocorrect(corrector, node) @@ -59,9 +59,9 @@ def on_resbody(node) def autocorrect(corrector, node) rescued, _, _body = *node - range = Parser::Source::Range.new(node.loc.expression.source_buffer, + range = Parser::Source::Range.new(node.source_range.source_buffer, node.loc.keyword.end_pos, - rescued.loc.expression.end_pos) + rescued.source_range.end_pos) corrector.replace(range, correction(*rescued)) end diff --git a/lib/rubocop/cop/lint/safe_navigation_consistency.rb b/lib/rubocop/cop/lint/safe_navigation_consistency.rb index b1b32a393e79..deb1b5209268 100644 --- a/lib/rubocop/cop/lint/safe_navigation_consistency.rb +++ b/lib/rubocop/cop/lint/safe_navigation_consistency.rb @@ -65,7 +65,7 @@ def autocorrect(corrector, node) end def location(node, unsafe_method_call) - node.loc.expression.join(unsafe_method_call.loc.expression) + node.source_range.join(unsafe_method_call.source_range) end def top_conditional_ancestor(node) diff --git a/lib/rubocop/cop/lint/script_permission.rb b/lib/rubocop/cop/lint/script_permission.rb index 2d5a60a06595..d724220f939a 100644 --- a/lib/rubocop/cop/lint/script_permission.rb +++ b/lib/rubocop/cop/lint/script_permission.rb @@ -53,7 +53,7 @@ def on_new_investigation private def autocorrect(comment) - FileUtils.chmod('+x', comment.loc.expression.source_buffer.name) + FileUtils.chmod('+x', comment.source_range.source_buffer.name) end def executable?(processed_source) diff --git a/lib/rubocop/cop/lint/shadowed_exception.rb b/lib/rubocop/cop/lint/shadowed_exception.rb index b6bd8a9f3d14..7de1be5ae7aa 100644 --- a/lib/rubocop/cop/lint/shadowed_exception.rb +++ b/lib/rubocop/cop/lint/shadowed_exception.rb @@ -83,7 +83,7 @@ def on_rescue(node) def offense_range(rescues) shadowing_rescue = find_shadowing_rescue(rescues) - expression = shadowing_rescue.loc.expression + expression = shadowing_rescue.source_range range_between(expression.begin_pos, expression.end_pos) end diff --git a/lib/rubocop/cop/lint/syntax.rb b/lib/rubocop/cop/lint/syntax.rb index 1c25a5b7d98d..457fac1c6e4f 100644 --- a/lib/rubocop/cop/lint/syntax.rb +++ b/lib/rubocop/cop/lint/syntax.rb @@ -33,6 +33,10 @@ def beautify_message(message) message << '.' unless message.end_with?('.') message end + + def find_severity(_range, _severity) + :fatal + end end end end diff --git a/lib/rubocop/cop/lint/to_enum_arguments.rb b/lib/rubocop/cop/lint/to_enum_arguments.rb index 0a697257a909..8269cf22fafa 100644 --- a/lib/rubocop/cop/lint/to_enum_arguments.rb +++ b/lib/rubocop/cop/lint/to_enum_arguments.rb @@ -14,8 +14,14 @@ module Lint # # # good # def foo(x, y = 1) + # # Alternatives to `__callee__` are `__method__` and `:foo`. # return to_enum(__callee__, x, y) - # # alternatives to `__callee__` are `__method__` and `:foo` + # end + # + # # good + # def foo(x, y = 1) + # # It is also allowed if it is wrapped in some method like Sorbet. + # return to_enum(T.must(__callee__), x, y) # end # class ToEnumArguments < Base @@ -43,8 +49,12 @@ def on_send(node) return unless def_node enum_conversion_call?(node) do |method_node, arguments| - add_offense(node) unless method_name?(method_node, def_node.method_name) && - arguments_match?(arguments, def_node) + next if method_node.call_type? && + !method_node.method?(:__method__) && !method_node.method?(:__callee__) + next if method_name?(method_node, def_node.method_name) && + arguments_match?(arguments, def_node) + + add_offense(node) end end diff --git a/lib/rubocop/cop/lint/unreachable_loop.rb b/lib/rubocop/cop/lint/unreachable_loop.rb index e54ac64f19a0..fe69c5ef50a0 100644 --- a/lib/rubocop/cop/lint/unreachable_loop.rb +++ b/lib/rubocop/cop/lint/unreachable_loop.rb @@ -111,9 +111,9 @@ def loop_method?(node) return false unless node.block_type? || node.numblock_type? send_node = node.send_node - return false if matches_allowed_pattern?(send_node.source) - - send_node.enumerable_method? || send_node.enumerator_method? || send_node.method?(:loop) + loopable = send_node.enumerable_method? || send_node.enumerator_method? || + send_node.method?(:loop) + loopable && !matches_allowed_pattern?(send_node.source) end def check(node) diff --git a/lib/rubocop/cop/lint/useless_access_modifier.rb b/lib/rubocop/cop/lint/useless_access_modifier.rb index 30790e10a4c4..b89a9d178a90 100644 --- a/lib/rubocop/cop/lint/useless_access_modifier.rb +++ b/lib/rubocop/cop/lint/useless_access_modifier.rb @@ -137,7 +137,7 @@ def on_class(node) alias on_sclass on_class def on_block(node) - return unless eval_call?(node) + return unless eval_call?(node) || included_block?(node) check_node(node.body) end @@ -167,11 +167,6 @@ def autocorrect(corrector, node) ({block numblock} (send _ {:class_eval :instance_eval}) ...) PATTERN - # @!method class_or_module_or_struct_new_call?(node) - def_node_matcher :class_or_module_or_struct_new_call?, <<~PATTERN - ({block numblock} (send (const {nil? cbase} {:Class :Module :Struct}) :new ...) ...) - PATTERN - def check_node(node) return if node.nil? @@ -197,10 +192,13 @@ def check_scope(node) end end + # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity def check_child_nodes(node, unused, cur_vis) node.child_nodes.each do |child| if child.send_type? && access_modifier?(child) cur_vis, unused = check_send_node(child, cur_vis, unused) + elsif child.block_type? && included_block?(child) + next elsif method_definition?(child) unused = nil elsif start_of_new_scope?(child) @@ -212,6 +210,7 @@ def check_child_nodes(node, unused, cur_vis) [cur_vis, unused] end + # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity def check_send_node(node, cur_vis, unused) if node.bare_access_modifier? @@ -245,6 +244,10 @@ def check_new_visibility(node, unused, new_vis, cur_vis) [new_vis, unused] end + def included_block?(block_node) + active_support_extensions_enabled? && block_node.method?(:included) + end + def method_definition?(child) static_method_definition?(child) || dynamic_method_definition?(child) || @@ -270,7 +273,7 @@ def start_of_new_scope?(child) def eval_call?(child) class_or_instance_eval?(child) || - class_or_module_or_struct_new_call?(child) || + child.class_constructor? || any_context_creating_methods?(child) end diff --git a/lib/rubocop/cop/lint/useless_method_definition.rb b/lib/rubocop/cop/lint/useless_method_definition.rb index fe365ef95a22..48bb66208d43 100644 --- a/lib/rubocop/cop/lint/useless_method_definition.rb +++ b/lib/rubocop/cop/lint/useless_method_definition.rb @@ -41,17 +41,25 @@ class UselessMethodDefinition < Base MSG = 'Useless method definition detected.' def on_def(node) - return if optional_args?(node) + return if method_definition_with_modifier?(node) || use_rest_or_optional_args?(node) return unless delegating?(node.body, node) - add_offense(node) { |corrector| corrector.remove(node) } + add_offense(node) do |corrector| + range = node.parent&.send_type? ? node.parent : node + + corrector.remove(range) + end end alias on_defs on_def private - def optional_args?(node) - node.arguments.any? { |arg| arg.optarg_type? || arg.kwoptarg_type? } + def method_definition_with_modifier?(node) + node.parent&.send_type? && !node.parent&.non_bare_access_modifier? + end + + def use_rest_or_optional_args?(node) + node.arguments.any? { |arg| arg.restarg_type? || arg.optarg_type? || arg.kwoptarg_type? } end def delegating?(node, def_node) diff --git a/lib/rubocop/cop/lint/useless_rescue.rb b/lib/rubocop/cop/lint/useless_rescue.rb new file mode 100644 index 000000000000..cf6caf9eeef6 --- /dev/null +++ b/lib/rubocop/cop/lint/useless_rescue.rb @@ -0,0 +1,89 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Lint + # Checks for useless `rescue`s, which only reraise rescued exceptions. + # + # @example + # # bad + # def foo + # do_something + # rescue + # raise + # end + # + # # bad + # def foo + # do_something + # rescue => e + # raise # or 'raise e', or 'raise $!', or 'raise $ERROR_INFO' + # end + # + # # good + # def foo + # do_something + # rescue + # do_cleanup + # raise + # end + # + # # bad (latest rescue) + # def foo + # do_something + # rescue ArgumentError + # # noop + # rescue + # raise + # end + # + # # good (not the latest rescue) + # def foo + # do_something + # rescue ArgumentError + # raise + # rescue + # # noop + # end + # + class UselessRescue < Base + MSG = 'Useless `rescue` detected.' + + def on_rescue(node) + resbody_node = node.resbody_branches.last + add_offense(resbody_node) if only_reraising?(resbody_node) + end + + private + + # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity + def only_reraising?(resbody_node) + return false if use_exception_variable_in_ensure?(resbody_node) + + body = resbody_node.body + + return false if body.nil? || !body.send_type? || !body.method?(:raise) || body.receiver + return true unless body.arguments? + return false if body.arguments.size > 1 + + exception_name = body.first_argument.source + + exception_objects(resbody_node).include?(exception_name) + end + # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity + + def use_exception_variable_in_ensure?(resbody_node) + return false unless (exception_variable = resbody_node.exception_variable) + return false unless (ensure_node = resbody_node.each_ancestor(:ensure).first) + return false unless (ensure_body = ensure_node.body) + + ensure_body.each_descendant(:lvar).map(&:source).include?(exception_variable.source) + end + + def exception_objects(resbody_node) + [resbody_node.exception_variable&.source, '$!', '$ERROR_INFO'] + end + end + end + end +end diff --git a/lib/rubocop/cop/lint/useless_ruby2_keywords.rb b/lib/rubocop/cop/lint/useless_ruby2_keywords.rb index b7d935991335..bea0aa26a7c1 100644 --- a/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +++ b/lib/rubocop/cop/lint/useless_ruby2_keywords.rb @@ -98,7 +98,7 @@ def inspect_sym(node, sym_node) return unless node.parent method_name = sym_node.value - definition = node.parent.each_child_node.detect { |n| method_definition(n, method_name) } + definition = find_method_definition(node, method_name) return unless definition return if allowed_arguments(definition.arguments) @@ -106,6 +106,14 @@ def inspect_sym(node, sym_node) add_offense(node, message: format(MSG, method_name: method_name)) end + def find_method_definition(node, method_name) + node.each_ancestor.lazy.map do |ancestor| + ancestor.each_child_node(:def, :block, :numblock).find do |child| + method_definition(child, method_name) + end + end.find(&:itself) + end + # `ruby2_keywords` is only allowed if there's a `restarg` and no keyword arguments def allowed_arguments(arguments) return false if arguments.empty? diff --git a/lib/rubocop/cop/lint/useless_times.rb b/lib/rubocop/cop/lint/useless_times.rb index 9f404c8e9cfa..bde3385f4cf2 100644 --- a/lib/rubocop/cop/lint/useless_times.rb +++ b/lib/rubocop/cop/lint/useless_times.rb @@ -74,7 +74,7 @@ def never_process?(count, node) end def remove_node(corrector, node) - corrector.remove(range_by_whole_lines(node.loc.expression, include_final_newline: true)) + corrector.remove(range_by_whole_lines(node.source_range, include_final_newline: true)) end def autocorrect_block_pass(corrector, node, proc_name) diff --git a/lib/rubocop/cop/lint/void.rb b/lib/rubocop/cop/lint/void.rb index ba39b4dbbc70..1e9d6933792a 100644 --- a/lib/rubocop/cop/lint/void.rb +++ b/lib/rubocop/cop/lint/void.rb @@ -46,19 +46,23 @@ class Void < Base LIT_MSG = 'Literal `%s` used in void context.' SELF_MSG = '`self` used in void context.' EXPRESSION_MSG = '`%s` used in void context.' - NONMUTATING_MSG = 'Method `#%s` used in void context. Did you mean `#%s!`?' + NONMUTATING_MSG = 'Method `#%s` used in void context. Did you mean `#%s`?' BINARY_OPERATORS = %i[* / % + - == === != < > <= >= <=>].freeze UNARY_OPERATORS = %i[+@ -@ ~ !].freeze OPERATORS = (BINARY_OPERATORS + UNARY_OPERATORS).freeze VOID_CONTEXT_TYPES = %i[def for block].freeze - NONMUTATING_METHODS = %i[capitalize chomp chop collect compact - delete_prefix delete_suffix downcase - encode flatten gsub lstrip map merge next - reject reverse rotate rstrip scrub select - shuffle slice sort sort_by squeeze strip sub - succ swapcase tr tr_s transform_values - unicode_normalize uniq upcase].freeze + NONMUTATING_METHODS_WITH_BANG_VERSION = %i[capitalize chomp chop compact + delete_prefix delete_suffix downcase + encode flatten gsub lstrip merge next + reject reverse rotate rstrip scrub select + shuffle slice sort sort_by squeeze strip sub + succ swapcase tr tr_s transform_values + unicode_normalize uniq upcase].freeze + METHODS_REPLACEABLE_BY_EACH = %i[collect map].freeze + + NONMUTATING_METHODS = (NONMUTATING_METHODS_WITH_BANG_VERSION + + METHODS_REPLACEABLE_BY_EACH).freeze def on_block(node) return unless node.body && !node.body.begin_type? @@ -124,9 +128,18 @@ def check_void_expression(node) end def check_nonmutating(node) - return unless node.send_type? && NONMUTATING_METHODS.include?(node.method_name) - - add_offense(node, message: format(NONMUTATING_MSG, method: node.method_name)) + return unless node.respond_to?(:method_name) + + method_name = node.method_name + return unless NONMUTATING_METHODS.include?(method_name) + + suggestion = if METHODS_REPLACEABLE_BY_EACH.include?(method_name) + 'each' + else + "#{method_name}!" + end + add_offense(node, + message: format(NONMUTATING_MSG, method: method_name, suggest: suggestion)) end def in_void_context?(node) diff --git a/lib/rubocop/cop/metrics/block_length.rb b/lib/rubocop/cop/metrics/block_length.rb index 23fd73c7cedd..11c9957cff3f 100644 --- a/lib/rubocop/cop/metrics/block_length.rb +++ b/lib/rubocop/cop/metrics/block_length.rb @@ -51,7 +51,7 @@ class BlockLength < Base def on_block(node) return if allowed_method?(node.method_name) || matches_allowed_pattern?(node.method_name) return if method_receiver_excluded?(node) - return if node.class_constructor? || node.struct_constructor? + return if node.class_constructor? check_code_length(node) end diff --git a/lib/rubocop/cop/metrics/block_nesting.rb b/lib/rubocop/cop/metrics/block_nesting.rb index 6808274b61d3..2c9777d01e9f 100644 --- a/lib/rubocop/cop/metrics/block_nesting.rb +++ b/lib/rubocop/cop/metrics/block_nesting.rb @@ -12,7 +12,7 @@ module Metrics # # The maximum level of nesting allowed is configurable. class BlockNesting < Base - NESTING_BLOCKS = %i[case if while while_post until until_post for resbody].freeze + NESTING_BLOCKS = %i[case case_match if while while_post until until_post for resbody].freeze exclude_limit 'Max' @@ -44,7 +44,7 @@ def check_nesting_level(node, max, current_level) def consider_node?(node) return true if NESTING_BLOCKS.include?(node.type) - count_blocks? && node.block_type? + count_blocks? && (node.block_type? || node.numblock_type?) end def message(max) diff --git a/lib/rubocop/cop/metrics/collection_literal_length.rb b/lib/rubocop/cop/metrics/collection_literal_length.rb new file mode 100644 index 000000000000..73ff9e04c04e --- /dev/null +++ b/lib/rubocop/cop/metrics/collection_literal_length.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Metrics + # Checks for literals with extremely many entries. This is indicative of + # configuration or data that may be better extracted somewhere else, like + # a database, fetched from an API, or read from a non-code file (CSV, + # JSON, YAML, etc.). + # + # @example + # # bad + # # Huge Array literal + # [1, 2, '...', 999_999_999] + # + # # bad + # # Huge Hash literal + # { 1 => 1, 2 => 2, '...' => '...', 999_999_999 => 999_999_999} + # + # # bad + # # Huge Set "literal" + # Set[1, 2, '...', 999_999_999] + # + # # good + # # Reasonably sized Array literal + # [1, 2, '...', 10] + # + # # good + # # Reading huge Array from external data source + # # File.readlines('numbers.txt', chomp: true).map!(&:to_i) + # + # # good + # # Reasonably sized Hash literal + # { 1 => 1, 2 => 2, '...' => '...', 10 => 10} + # + # # good + # # Reading huge Hash from external data source + # CSV.foreach('numbers.csv', headers: true).each_with_object({}) do |row, hash| + # hash[row["key"].to_i] = row["value"].to_i + # end + # + # # good + # # Reasonably sized Set "literal" + # Set[1, 2, '...', 10] + # + # # good + # # Reading huge Set from external data source + # SomeFramework.config_for(:something)[:numbers].to_set + # + class CollectionLiteralLength < Base + MSG = 'Avoid hard coding large quantities of data in code. ' \ + 'Prefer reading the data from an external source.' + RESTRICT_ON_SEND = [:[]].freeze + + def on_array(node) + add_offense(node) if node.children.length >= collection_threshold + end + alias on_hash on_array + + def on_index(node) + add_offense(node) if node.arguments.length >= collection_threshold + end + + def on_send(node) + on_index(node) if node.method?(:[]) + end + + private + + def collection_threshold + cop_config.fetch('LengthThreshold', Float::INFINITY) + end + end + end + end +end diff --git a/lib/rubocop/cop/metrics/cyclomatic_complexity.rb b/lib/rubocop/cop/metrics/cyclomatic_complexity.rb index 9aa066621349..6207785ba1cd 100644 --- a/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +++ b/lib/rubocop/cop/metrics/cyclomatic_complexity.rb @@ -35,7 +35,7 @@ class CyclomaticComplexity < Base MSG = 'Cyclomatic complexity for %s is too high. [%d/%d]' COUNTED_NODES = %i[if while until for csend block block_pass - rescue when and or or_asgn and_asgn].freeze + rescue when in_pattern and or or_asgn and_asgn].freeze private diff --git a/lib/rubocop/cop/metrics/parameter_lists.rb b/lib/rubocop/cop/metrics/parameter_lists.rb index 6ff025f1ff99..5ded2f86a897 100644 --- a/lib/rubocop/cop/metrics/parameter_lists.rb +++ b/lib/rubocop/cop/metrics/parameter_lists.rb @@ -9,6 +9,20 @@ module Metrics # Keyword arguments can optionally be excluded from the total count, # as they add less complexity than positional or optional parameters. # + # Any number of arguments for `initialize` method inside a block of + # `Struct.new` and `Data.define` like this is always allowed: + # + # [source,ruby] + # ---- + # Struct.new(:one, :two, :three, :four, :five, keyword_init: true) do + # def initialize(one:, two:, three:, four:, five:) + # end + # end + # ---- + # + # This is because checking the number of arguments of the `initialize` method + # does not make sense. + # # NOTE: Explicit block argument `&block` is not counted to prevent # erroneous change that is avoided by making block argument implicit. # @@ -63,6 +77,16 @@ class ParameterLists < Base NAMED_KEYWORD_TYPES = %i[kwoptarg kwarg].freeze private_constant :NAMED_KEYWORD_TYPES + # @!method struct_new_or_data_define_block?(node) + def_node_matcher :struct_new_or_data_define_block?, <<~PATTERN + (block + { + (send (const {nil? cbase} :Struct) :new ...) + (send (const {nil? cbase} :Data) :define ...) + } + (args) ...) + PATTERN + def on_def(node) optargs = node.arguments.select(&:optarg_type?) return if optargs.count <= max_optional_parameters @@ -78,6 +102,9 @@ def on_def(node) alias on_defs on_def def on_args(node) + parent = node.parent + return if parent.method?(:initialize) && struct_new_or_data_define_block?(parent.parent) + count = args_count(node) return unless count > max_params diff --git a/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb b/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb index c520cc0bca26..ef58175891ad 100644 --- a/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +++ b/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb @@ -25,10 +25,7 @@ class AbcSizeCalculator # > http://c2.com/cgi/wiki?AbcMetric CONDITION_NODES = CyclomaticComplexity::COUNTED_NODES.freeze - # TODO: move to rubocop-ast - ARGUMENT_TYPES = %i[arg optarg restarg kwarg kwoptarg kwrestarg blockarg].freeze - - private_constant :BRANCH_NODES, :CONDITION_NODES, :ARGUMENT_TYPES + private_constant :BRANCH_NODES, :CONDITION_NODES def self.calculate(node, discount_repeated_attributes: false) new(node, discount_repeated_attributes: discount_repeated_attributes).calculate @@ -129,7 +126,7 @@ def branch?(node) end def argument?(node) - ARGUMENT_TYPES.include?(node.type) && capturing_variable?(node.children.first) + node.argument_type? && capturing_variable?(node.children.first) end def condition?(node) diff --git a/lib/rubocop/cop/metrics/utils/code_length_calculator.rb b/lib/rubocop/cop/metrics/utils/code_length_calculator.rb index cd0474649337..0155956ea96e 100644 --- a/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +++ b/lib/rubocop/cop/metrics/utils/code_length_calculator.rb @@ -163,8 +163,8 @@ def omit_length(descendant) return 0 unless parenthesized?(parent) [ - parent.loc.begin.end_pos != descendant.loc.expression.begin_pos, - parent.loc.end.begin_pos != descendant.loc.expression.end_pos + parent.loc.begin.end_pos != descendant.source_range.begin_pos, + parent.loc.end.begin_pos != descendant.source_range.end_pos ].count(true) end diff --git a/lib/rubocop/cop/migration/department_name.rb b/lib/rubocop/cop/migration/department_name.rb index 4cc334342853..a8428b06d4cb 100644 --- a/lib/rubocop/cop/migration/department_name.rb +++ b/lib/rubocop/cop/migration/department_name.rb @@ -45,7 +45,7 @@ def disable_comment_offset end def check_cop_name(name, comment, offset) - start = comment.location.expression.begin_pos + offset + start = comment.source_range.begin_pos + offset range = range_between(start, start + name.length) add_offense(range) do |corrector| diff --git a/lib/rubocop/cop/mixin/alignment.rb b/lib/rubocop/cop/mixin/alignment.rb index 745883c82b3f..231aeb65e25f 100644 --- a/lib/rubocop/cop/mixin/alignment.rb +++ b/lib/rubocop/cop/mixin/alignment.rb @@ -12,7 +12,7 @@ module Alignment attr_reader :column_delta def configured_indentation_width - cop_config['IndentationWidth'] || config.for_cop('Layout/IndentationWidth')['Width'] + cop_config['IndentationWidth'] || config.for_cop('Layout/IndentationWidth')['Width'] || 2 end def indentation(node) diff --git a/lib/rubocop/cop/mixin/allowed_methods.rb b/lib/rubocop/cop/mixin/allowed_methods.rb index 0aaeec4d1b78..5d0e3f8a5d6e 100644 --- a/lib/rubocop/cop/mixin/allowed_methods.rb +++ b/lib/rubocop/cop/mixin/allowed_methods.rb @@ -3,7 +3,9 @@ module RuboCop module Cop # This module encapsulates the ability to allow certain methods when - # parsing. + # parsing. Even if the code is in offense, if it contains methods + # that are allowed. This module is equivalent to the IgnoredMethods module, + # which will be deprecated in RuboCop 2.0. module AllowedMethods private diff --git a/lib/rubocop/cop/mixin/annotation_comment.rb b/lib/rubocop/cop/mixin/annotation_comment.rb index 5370e06f04ed..5ab492d3ae05 100644 --- a/lib/rubocop/cop/mixin/annotation_comment.rb +++ b/lib/rubocop/cop/mixin/annotation_comment.rb @@ -29,7 +29,7 @@ def correct?(colon:) # Returns the range bounds for just the annotation def bounds - start = comment.loc.expression.begin_pos + margin.length + start = comment.source_range.begin_pos + margin.length length = [keyword, colon, space].reduce(0) { |len, elem| len + elem.to_s.length } [start, start + length] end diff --git a/lib/rubocop/cop/mixin/code_length.rb b/lib/rubocop/cop/mixin/code_length.rb index bfb87c072198..b9734e49fcf3 100644 --- a/lib/rubocop/cop/mixin/code_length.rb +++ b/lib/rubocop/cop/mixin/code_length.rb @@ -36,7 +36,7 @@ def check_code_length(node) length = calculator.calculate return if length <= max_length - location = node.casgn_type? ? node.loc.name : node.loc.expression + location = node.casgn_type? ? node.loc.name : node.source_range add_offense(location, message: message(length, max_length)) { self.max = length } end diff --git a/lib/rubocop/cop/mixin/comments_help.rb b/lib/rubocop/cop/mixin/comments_help.rb index aeef59ca20b5..feee77012c9a 100644 --- a/lib/rubocop/cop/mixin/comments_help.rb +++ b/lib/rubocop/cop/mixin/comments_help.rb @@ -37,7 +37,7 @@ def comments_contain_disables?(node, cop_name) private def end_position_for(node) - end_line = buffer.line_for_position(node.loc.expression.end_pos) + end_line = buffer.line_for_position(node.source_range.end_pos) buffer.line_range(end_line).end_pos end @@ -62,19 +62,21 @@ def buffer # Returns the end line of a node, which might be a comment and not part of the AST # End line is considered either the line at which another node starts, or # the line at which the parent node ends. - # rubocop:disable Metrics/AbcSize + # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity def find_end_line(node) - if node.if_type? && node.loc.else + if node.if_type? && node.else? node.loc.else.line - elsif (next_sibling = node.right_sibling) + elsif node.if_type? && node.ternary? + node.else_branch.loc.line + elsif (next_sibling = node.right_sibling) && next_sibling.is_a?(AST::Node) next_sibling.loc.line elsif (parent = node.parent) - parent.loc.end ? parent.loc.end.line : parent.loc.line + parent.loc.respond_to?(:end) && parent.loc.end ? parent.loc.end.line : parent.loc.line else node.loc.end.line end end - # rubocop:enable Metrics/AbcSize + # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity end end end diff --git a/lib/rubocop/cop/mixin/documentation_comment.rb b/lib/rubocop/cop/mixin/documentation_comment.rb index b22e6cfb8d50..b7959a46d72e 100644 --- a/lib/rubocop/cop/mixin/documentation_comment.rb +++ b/lib/rubocop/cop/mixin/documentation_comment.rb @@ -23,7 +23,7 @@ def documentation_comment?(node) # The args node1 & node2 may represent a RuboCop::AST::Node # or a Parser::Source::Comment. Both respond to #loc. def preceding_comment?(node1, node2) - node1 && node2 && precede?(node2, node1) && comment_line?(node2.loc.expression.source) + node1 && node2 && precede?(node2, node1) && comment_line?(node2.source) end # The args node1 & node2 may represent a RuboCop::AST::Node diff --git a/lib/rubocop/cop/mixin/hash_alignment_styles.rb b/lib/rubocop/cop/mixin/hash_alignment_styles.rb index 10725f3b19f8..8f12191fb9c7 100644 --- a/lib/rubocop/cop/mixin/hash_alignment_styles.rb +++ b/lib/rubocop/cop/mixin/hash_alignment_styles.rb @@ -33,7 +33,7 @@ def deltas(first_pair, current_pair) def separator_delta(pair) if pair.hash_rocket? - correct_separator_column = pair.key.loc.expression.end.column + 1 + correct_separator_column = pair.key.source_range.end.column + 1 actual_separator_column = pair.loc.operator.column correct_separator_column - actual_separator_column diff --git a/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb b/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb index f264961b195f..651af331fddb 100644 --- a/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +++ b/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb @@ -3,6 +3,7 @@ module RuboCop module Cop # This module checks for Ruby 3.1's hash value omission syntax. + # rubocop:disable Metrics/ModuleLength module HashShorthandSyntax OMIT_HASH_VALUE_MSG = 'Omit the hash value.' EXPLICIT_HASH_VALUE_MSG = 'Include the hash value.' @@ -45,21 +46,22 @@ def on_pair(node) private - # rubocop:disable Metrics/AbcSize - def register_offense(node, message, replacement) + def register_offense(node, message, replacement) # rubocop:disable Metrics/AbcSize add_offense(node.value, message: message) do |corrector| if (def_node = def_node_that_require_parentheses(node)) - white_spaces = range_between(def_node.loc.selector.end_pos, + last_argument = def_node.last_argument + if last_argument.nil? || !last_argument.hash_type? + next corrector.replace(node, replacement) + end + + white_spaces = range_between(def_node.selector.end_pos, def_node.first_argument.source_range.begin_pos) corrector.replace(white_spaces, '(') - - last_argument = def_node.arguments.last corrector.insert_after(last_argument, ')') if node == last_argument.pairs.last end corrector.replace(node, replacement) end end - # rubocop:enable Metrics/AbcSize def ignore_mixed_hash_shorthand_syntax?(hash_node) target_ruby_version <= 3.0 || enforced_shorthand_syntax != 'consistent' || @@ -86,25 +88,39 @@ def require_hash_value?(hash_key_source, node) end def require_hash_value_for_around_hash_literal?(node) - return false unless (send_node = find_ancestor_send_node(node)) + return false unless (method_dispatch_node = find_ancestor_method_dispatch_node(node)) - !node.parent.braces? && !use_element_of_hash_literal_as_receiver?(send_node, node.parent) && - use_modifier_form_without_parenthesized_method_call?(send_node) + !node.parent.braces? && + !use_element_of_hash_literal_as_receiver?(method_dispatch_node, node.parent) && + use_modifier_form_without_parenthesized_method_call?(method_dispatch_node) end + # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity def def_node_that_require_parentheses(node) - return unless (send_node = find_ancestor_send_node(node)) - return unless without_parentheses_call_expr_follows?(send_node) + last_pair = node.parent.pairs.last + return unless last_pair.key.source == last_pair.value.source + return unless (dispatch_node = find_ancestor_method_dispatch_node(node)) + return if dispatch_node.assignment_method? + return if dispatch_node.parenthesized? + return if dispatch_node.parent && parentheses?(dispatch_node.parent) + return if last_expression?(dispatch_node) && !method_dispatch_as_argument?(dispatch_node) - def_node = node.each_ancestor(:send, :csend).first + def_node = node.each_ancestor(:send, :csend, :super, :yield).first - def_node unless def_node && def_node.arguments.empty? + DefNode.new(def_node) unless def_node && def_node.arguments.empty? end + # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity - def find_ancestor_send_node(node) - ancestor = node.parent.parent + def find_ancestor_method_dispatch_node(node) + return unless (ancestor = node.parent.parent) + return unless ancestor.call_type? || ancestor.super_type? || ancestor.yield_type? + return if brackets?(ancestor) + + ancestor + end - ancestor if ancestor&.call_type? && !ancestor&.method?(:[]) + def brackets?(method_dispatch_node) + method_dispatch_node.method?(:[]) || method_dispatch_node.method?(:[]=) end def use_element_of_hash_literal_as_receiver?(ancestor, parent) @@ -118,15 +134,19 @@ def use_modifier_form_without_parenthesized_method_call?(ancestor) ancestor.ancestors.any? { |node| node.respond_to?(:modifier_form?) && node.modifier_form? } end - def without_parentheses_call_expr_follows?(ancestor) - return false unless ancestor.respond_to?(:parenthesized?) && !ancestor.parenthesized? + def last_expression?(node) + return false if node.right_sibling + return true unless (assignment_node = node.each_ancestor.find(&:assignment?)) + return last_expression?(assignment_node.parent) if assignment_node.parent&.assignment? - right_sibling = ancestor.right_sibling - right_sibling ||= ancestor.each_ancestor.find do |node| - node.assignment? || node.send_type? - end&.right_sibling + !assignment_node.right_sibling + end + + def method_dispatch_as_argument?(method_dispatch_node) + parent = method_dispatch_node.parent + return false unless parent - !!right_sibling + parent.call_type? || parent.super_type? || parent.yield_type? end def breakdown_value_types_of_hash(hash_node) @@ -182,6 +202,25 @@ def no_mixed_shorthand_syntax_check(hash_value_type_breakdown) register_offense(pair_node, OMIT_HASH_VALUE_MSG, replacement) end end + + DefNode = Struct.new(:node) do + def selector + if node.loc.respond_to?(:selector) + node.loc.selector + else + node.loc.keyword + end + end + + def first_argument + node.first_argument + end + + def last_argument + node.last_argument + end + end end end + # rubocop:enable Metrics/ModuleLength end diff --git a/lib/rubocop/cop/mixin/hash_transform_method.rb b/lib/rubocop/cop/mixin/hash_transform_method.rb index 2e6a36c3a12f..75b2716721d6 100644 --- a/lib/rubocop/cop/mixin/hash_transform_method.rb +++ b/lib/rubocop/cop/mixin/hash_transform_method.rb @@ -159,7 +159,7 @@ def self.from_to_h(node, match) end def strip_prefix_and_suffix(node, corrector) - expression = node.loc.expression + expression = node.source_range corrector.remove_leading(expression, leading) corrector.remove_trailing(expression, trailing) end @@ -175,11 +175,11 @@ def set_new_method_name(new_method_name, corrector) end def set_new_arg_name(transformed_argname, corrector) - corrector.replace(block_node.arguments.loc.expression, "|#{transformed_argname}|") + corrector.replace(block_node.arguments, "|#{transformed_argname}|") end def set_new_body_expression(transforming_body_expr, corrector) - body_source = transforming_body_expr.loc.expression.source + body_source = transforming_body_expr.source if transforming_body_expr.hash_type? && !transforming_body_expr.braces? body_source = "{ #{body_source} }" end diff --git a/lib/rubocop/cop/mixin/line_length_help.rb b/lib/rubocop/cop/mixin/line_length_help.rb index 8715871936a2..a9ba7472dff1 100644 --- a/lib/rubocop/cop/mixin/line_length_help.rb +++ b/lib/rubocop/cop/mixin/line_length_help.rb @@ -4,6 +4,8 @@ module RuboCop module Cop # Help methods for determining if a line is too long. module LineLengthHelp + include Alignment + private def ignore_cop_directives? @@ -85,7 +87,7 @@ def extend_uri_end_position(line, end_position) def tab_indentation_width config.for_cop('Layout/IndentationStyle')['IndentationWidth'] || - config.for_cop('Layout/IndentationWidth')['Width'] + configured_indentation_width end def uri_regexp diff --git a/lib/rubocop/cop/mixin/min_branches_count.rb b/lib/rubocop/cop/mixin/min_branches_count.rb new file mode 100644 index 000000000000..cb7a1ab5fe23 --- /dev/null +++ b/lib/rubocop/cop/mixin/min_branches_count.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + # Common functionality for checking minimum branches count. + module MinBranchesCount + private + + def min_branches_count?(node) + branches = + if node.case_type? + node.when_branches + elsif node.if_type? + if_conditional_branches(node) + else + raise ArgumentError, "Unsupported #{node.type.inspect} node type" + end + + branches.size >= min_branches_count + end + + def min_branches_count + length = cop_config['MinBranchesCount'] || 3 + return length if length.is_a?(Integer) && length.positive? + + raise 'MinBranchesCount needs to be a positive integer!' + end + + def if_conditional_branches(node, branches = []) + return [] if node.nil? || !node.if_type? + + branches << node.if_branch + + else_branch = node.else_branch + if_conditional_branches(else_branch, branches) if else_branch&.if_type? + branches + end + end + end +end diff --git a/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb b/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb index 9005442de5ae..e15ac1267283 100644 --- a/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb +++ b/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb @@ -1,8 +1,5 @@ # frozen_string_literal: true -# TEAM: backend_infra -# WATCHERS: maxh - module RuboCop module Cop # Common functionality for checking for a line break before each diff --git a/lib/rubocop/cop/mixin/ordered_gem_node.rb b/lib/rubocop/cop/mixin/ordered_gem_node.rb index b93602b8fae7..09878478b712 100644 --- a/lib/rubocop/cop/mixin/ordered_gem_node.rb +++ b/lib/rubocop/cop/mixin/ordered_gem_node.rb @@ -10,7 +10,7 @@ module OrderedGemNode def get_source_range(node, comments_as_separators) unless comments_as_separators first_comment = processed_source.ast_with_comments[node].first - return first_comment.loc.expression unless first_comment.nil? + return first_comment.source_range unless first_comment.nil? end node.source_range end diff --git a/lib/rubocop/cop/mixin/range_help.rb b/lib/rubocop/cop/mixin/range_help.rb index e9c95168b282..efe1cd3afd73 100644 --- a/lib/rubocop/cop/mixin/range_help.rb +++ b/lib/rubocop/cop/mixin/range_help.rb @@ -132,12 +132,7 @@ def range_with_comments_and_lines(node) end def range_with_comments(node) - ranges = [ - node, - *@processed_source.ast_with_comments[node] - ].map do |element| - element.location.expression - end + ranges = [node, *@processed_source.ast_with_comments[node]].map(&:source_range) ranges.reduce do |result, range| add_range(result, range) end diff --git a/lib/rubocop/cop/mixin/statement_modifier.rb b/lib/rubocop/cop/mixin/statement_modifier.rb index f3757dc22dd1..dbdf6b39f42f 100644 --- a/lib/rubocop/cop/mixin/statement_modifier.rb +++ b/lib/rubocop/cop/mixin/statement_modifier.rb @@ -5,6 +5,7 @@ module Cop # Common functionality for modifier cops. module StatementModifier include LineLengthHelp + include RangeHelp private @@ -64,14 +65,14 @@ def if_body_source(if_body) end def method_source(if_body) - range_between(if_body.loc.expression.begin_pos, if_body.loc.selector.end_pos).source + range_between(if_body.source_range.begin_pos, if_body.loc.selector.end_pos).source end def first_line_comment(node) comment = processed_source.find_comment { |c| same_line?(c, node) } return unless comment - comment_source = comment.loc.expression.source + comment_source = comment.source comment_source unless comment_disables_cop?(comment_source) end diff --git a/lib/rubocop/cop/mixin/surrounding_space.rb b/lib/rubocop/cop/mixin/surrounding_space.rb index f7c465375c63..419b2fcb6219 100644 --- a/lib/rubocop/cop/mixin/surrounding_space.rb +++ b/lib/rubocop/cop/mixin/surrounding_space.rb @@ -107,9 +107,9 @@ def empty_offense(node, range, message, command) end end - def empty_brackets?(left_bracket_token, right_bracket_token) - left_index = processed_source.tokens.index(left_bracket_token) - right_index = processed_source.tokens.index(right_bracket_token) + def empty_brackets?(left_bracket_token, right_bracket_token, tokens: processed_source.tokens) + left_index = tokens.index(left_bracket_token) + right_index = tokens.index(right_bracket_token) right_index && left_index == right_index - 1 end diff --git a/lib/rubocop/cop/mixin/trailing_comma.rb b/lib/rubocop/cop/mixin/trailing_comma.rb index fb3965701806..79554e8f1766 100644 --- a/lib/rubocop/cop/mixin/trailing_comma.rb +++ b/lib/rubocop/cop/mixin/trailing_comma.rb @@ -75,7 +75,7 @@ def should_have_comma?(style, node) def inside_comment?(range, comma_offset) comment = processed_source.comment_at_line(range.line) - comment && comment.loc.expression.begin_pos < range.begin_pos + comma_offset + comment && comment.source_range.begin_pos < range.begin_pos + comma_offset end # Returns true if the node has round/square/curly brackets. @@ -132,7 +132,7 @@ def on_same_line?(range1, range2) def avoid_comma(kind, comma_begin_pos, extra_info) range = range_between(comma_begin_pos, comma_begin_pos + 1) - article = /array/.match?(kind) ? 'an' : 'a' + article = kind.include?('array') ? 'an' : 'a' msg = format( MSG, command: 'Avoid', diff --git a/lib/rubocop/cop/naming/block_forwarding.rb b/lib/rubocop/cop/naming/block_forwarding.rb index b5b86a44d5f2..83b9eb25b1bf 100644 --- a/lib/rubocop/cop/naming/block_forwarding.rb +++ b/lib/rubocop/cop/naming/block_forwarding.rb @@ -47,6 +47,10 @@ class BlockForwarding < Base MSG = 'Use %