From b62ccfc3eb18c2fd1f626365b86830aa78da7958 Mon Sep 17 00:00:00 2001 From: zverok Date: Thu, 30 Apr 2020 15:23:48 +0300 Subject: [PATCH] Implement Style/SlicingWithRange cop Under Ruby >= 2.6, proposes to change ary[x..-1] to ary[x..]. Supports autocorrect. --- CHANGELOG.md | 1 + config/default.yml | 6 +++ lib/rubocop.rb | 1 + lib/rubocop/cop/style/slicing_with_range.rb | 39 ++++++++++++++ manual/cops.md | 1 + manual/cops_style.md | 23 ++++++++ .../cop/style/slicing_with_range_spec.rb | 52 +++++++++++++++++++ 7 files changed, 123 insertions(+) create mode 100644 lib/rubocop/cop/style/slicing_with_range.rb create mode 100644 spec/rubocop/cop/style/slicing_with_range_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 72d13521593..07fe85951b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### New features +* [#7921](https://github.com/rubocop-hq/rubocop/pull/7921): Add [Style/SlicingWithRange] cop. ([@zverok][]) * [#7895](https://github.com/rubocop-hq/rubocop/pull/7895): Include `.simplecov` file by default. ([@robotdana][]) * [#7916](https://github.com/rubocop-hq/rubocop/pull/7916): Support autocorrection for `Lint/AmbiguousRegexpLiteral`. ([@koic][]) * [#7917](https://github.com/rubocop-hq/rubocop/pull/7917): Support autocorrection for `Lint/UselessAccessModifier`. ([@koic][]) diff --git a/config/default.yml b/config/default.yml index bc55a9f5c68..3fc664c8f28 100644 --- a/config/default.yml +++ b/config/default.yml @@ -3733,6 +3733,12 @@ Style/SingleLineMethods: VersionChanged: '0.19' AllowIfMethodIsEmpty: true +Style/SlicingWithRange: + Description: 'Checks array slicing is done with endless ranges when suitable.' + Enabled: pending + VersionAdded: '0.83' + Safe: false + Style/SpecialGlobalVars: Description: 'Avoid Perl-style global variables.' StyleGuide: '#no-cryptic-perlisms' diff --git a/lib/rubocop.rb b/lib/rubocop.rb index 080bb75d076..49438f2c519 100644 --- a/lib/rubocop.rb +++ b/lib/rubocop.rb @@ -542,6 +542,7 @@ require_relative 'rubocop/cop/style/signal_exception' require_relative 'rubocop/cop/style/single_line_block_params' require_relative 'rubocop/cop/style/single_line_methods' +require_relative 'rubocop/cop/style/slicing_with_range' require_relative 'rubocop/cop/style/special_global_vars' require_relative 'rubocop/cop/style/stabby_lambda_parentheses' require_relative 'rubocop/cop/style/stderr_puts' diff --git a/lib/rubocop/cop/style/slicing_with_range.rb b/lib/rubocop/cop/style/slicing_with_range.rb new file mode 100644 index 00000000000..9d8860d40bb --- /dev/null +++ b/lib/rubocop/cop/style/slicing_with_range.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Style + # This cop checks that arrays are sliced with endless ranges instead of + # `ary[start..-1]` on Ruby 2.6+. + # + # @example + # # bad + # items[1..-1] + # + # # good + # items[1..] + class SlicingWithRange < Cop + extend TargetRubyVersion + + minimum_target_ruby_version 2.6 + + MSG = 'Prefer ary[n..] over ary[n..-1].' + + def_node_matcher :range_till_minus_one?, '(irange (int _) (int -1))' + + def on_send(node) + return unless node.method?(:[]) && node.arguments.count == 1 + return unless range_till_minus_one?(node.arguments.first) + + add_offense(node.arguments.first) + end + + def autocorrect(node) + lambda do |corrector| + corrector.remove(node.end) + end + end + end + end + end +end diff --git a/manual/cops.md b/manual/cops.md index 39d720b1837..f61fe208e80 100644 --- a/manual/cops.md +++ b/manual/cops.md @@ -449,6 +449,7 @@ In the following section you find all available cops: * [Style/SignalException](cops_style.md#stylesignalexception) * [Style/SingleLineBlockParams](cops_style.md#stylesinglelineblockparams) * [Style/SingleLineMethods](cops_style.md#stylesinglelinemethods) +* [Style/SlicingWithRange](cops_style.md#styleslicingwithrange) * [Style/SpecialGlobalVars](cops_style.md#stylespecialglobalvars) * [Style/StabbyLambdaParentheses](cops_style.md#stylestabbylambdaparentheses) * [Style/StderrPuts](cops_style.md#stylestderrputs) diff --git a/manual/cops_style.md b/manual/cops_style.md index 056eb74509f..96b2ced4447 100644 --- a/manual/cops_style.md +++ b/manual/cops_style.md @@ -6609,6 +6609,29 @@ AllowIfMethodIsEmpty | `true` | Boolean * [https://rubystyle.guide#no-single-line-methods](https://rubystyle.guide#no-single-line-methods) +## Style/SlicingWithRange + +!!! Note + + Required Ruby version: 2.6 + +Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged +--- | --- | --- | --- | --- +Pending | No | Yes (Unsafe) | 0.83 | - + +This cop checks that arrays are sliced with endless ranges instead of +`ary[start..-1]` on Ruby 2.6+. + +### Examples + +```ruby +# bad +items[1..-1] + +# good +items[1..] +``` + ## Style/SpecialGlobalVars Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged diff --git a/spec/rubocop/cop/style/slicing_with_range_spec.rb b/spec/rubocop/cop/style/slicing_with_range_spec.rb new file mode 100644 index 00000000000..f72052a5624 --- /dev/null +++ b/spec/rubocop/cop/style/slicing_with_range_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +RSpec.describe RuboCop::Cop::Style::SlicingWithRange, :config do + subject(:cop) { described_class.new(config) } + + context '<= Ruby 2.5', :ruby25 do + it 'reports no offense for array slicing with -1' do + expect_no_offenses(<<~RUBY) + ary[1..-1] + RUBY + end + end + + context '>= Ruby 2.6', :ruby26 do + it 'reports an offense for slicing to ..-1' do + expect_offense(<<~RUBY) + ary[1..-1] + ^^^^^ Prefer ary[n..] over ary[n..-1]. + RUBY + + expect_correction(<<~RUBY) + ary[1..] + RUBY + end + + it 'reports no offense for excluding end' do + expect_no_offenses(<<~RUBY) + ary[1...-1] + RUBY + end + + it 'reports no offense for other methods' do + expect_no_offenses(<<~RUBY) + ary.push(1..-1) + RUBY + end + + it 'reports no offense for array with range inside' do + expect_no_offenses(<<~RUBY) + ranges = [1..-1] + RUBY + end + end + + context '>= Ruby 2.7', :ruby27 do + it 'reports no offense for startless' do + expect_no_offenses(<<~RUBY) + ary[..-1] + RUBY + end + end +end