Skip to content

Commit

Permalink
Add Lint/UselessArraySplat
Browse files Browse the repository at this point in the history
Also fixes #3069
  • Loading branch information
owst committed Apr 23, 2016
1 parent 603df7f commit b4eaaa6
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -18,6 +18,7 @@
* [#3052](https://github.com/bbatsov/rubocop/pull/3052): `Style/MultilineMethodCallBraceLayout` enforced style supports `same_line` option. ([@panthomakos][])
* [#3052](https://github.com/bbatsov/rubocop/pull/3052): `Style/MultilineMethodDefinitionBraceLayout` enforced style supports `same_line` option. ([@panthomakos][])
* [#3019](https://github.com/bbatsov/rubocop/issues/3019): Add new `Style/EmptyCaseCondition` cop. ([@owst][])
* [#3072](https://github.com/bbatsov/rubocop/pull/3072): Add new `Lint/UselessArraySplat` cop. ([@owst][])

### Bug fixes

Expand Down
4 changes: 4 additions & 0 deletions config/enabled.yml
Expand Up @@ -1139,6 +1139,10 @@ Lint/UselessAccessModifier:
Description: 'Checks for useless access modifiers.'
Enabled: true

Lint/UselessArraySplat:
Description: 'Checks for useless array splats.'
Enabled: true

Lint/UselessAssignment:
Description: 'Checks for useless assignment to a local variable.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars'
Expand Down
1 change: 1 addition & 0 deletions lib/rubocop.rb
Expand Up @@ -128,6 +128,7 @@
require 'rubocop/cop/lint/unused_block_argument'
require 'rubocop/cop/lint/unused_method_argument'
require 'rubocop/cop/lint/useless_access_modifier'
require 'rubocop/cop/lint/useless_array_splat'
require 'rubocop/cop/lint/useless_assignment'
require 'rubocop/cop/lint/useless_comparison'
require 'rubocop/cop/lint/useless_else_without_rescue'
Expand Down
56 changes: 56 additions & 0 deletions lib/rubocop/cop/lint/useless_array_splat.rb
@@ -0,0 +1,56 @@
# encoding: utf-8
# frozen_string_literal: true

module RuboCop
module Cop
module Lint
# This cop checks for unncessary array splats.
#
# @example
#
# # bad:
#
# a, b = *[1, 2, 3]
#
# # good:
#
# a, b = [1, 2, 3]
#
# # bad:
#
# a = *[1, 2, 3]
#
# # good:
#
# a = [1, 2, 3]
class UselessArraySplat < Cop
MSG = 'Unnecessary array splat.'.freeze
ARRAY_NEW_PATTERN = '(send (const nil :Array) :new ...)'.freeze

%w(m lv cv iv c gv).each do |var_type|
define_method("on_#{var_type}asgn") do |node|
*, rhs = *node

return unless rhs.is_a?(Node) && rhs.array_type?

add_offense(rhs, splat_source_range(rhs)) if array_splat?(rhs)
end
end

private

def_node_matcher :array_splat?, <<-PATTERN
(array (splat {(array ...) (block #{ARRAY_NEW_PATTERN} ...) #{ARRAY_NEW_PATTERN}} ...))
PATTERN

def splat_source_range(node)
node.loc.expression.begin.resize(1)
end

def autocorrect(node)
->(corrector) { corrector.remove(splat_source_range(node)) }
end
end
end
end
end
97 changes: 97 additions & 0 deletions spec/rubocop/cop/lint/useless_array_splat_spec.rb
@@ -0,0 +1,97 @@
# encoding: utf-8
# frozen_string_literal: true

require 'spec_helper'

describe RuboCop::Cop::Lint::UselessArraySplat do
subject(:cop) { described_class.new }

shared_examples 'detecting and correcting offenses' do
let(:source) { "#{binding} = *#{source_rhs}" }

it 'registers an offense and autocorrects' do
inspect_source(cop, source)

expect(cop.offenses.size).to eq(1)
expect(cop.offenses.first.message).to eq('Unnecessary array splat.')
expect(cop.highlights).to eq(['*'])
expect(autocorrect_source(cop, source)).to eq(source.delete('*'))
end
end

shared_examples 'detect and correct no offenses' do
let(:source) { "#{binding} = #{source_rhs}" }

it 'registers and corrects no offenses' do
inspect_source(cop, source)

expect(cop.offenses).to be_empty
expect(autocorrect_source(cop, source)).to eq(source)
end
end

{
'variable' => 'a',
'instance variable' => '@a',
'class variable' => '@@a',
'constant' => 'A',
'global variable' => '$a'
}.each do |type, var|
{
'a single' => var,
'more than one' => [var, var].join(', ')
}.each do |count, binding|
context "when splatting into #{count} #{type}" do
let(:binding) { binding }

context 'with splat' do
context 'for an array literal' do
let(:source_rhs) { '[1, 2, 3]' }

include_examples 'detecting and correcting offenses'
end

context 'for a constructed Array' do
let(:source_rhs) { 'Array.new(3) { 42 }' }

include_examples 'detecting and correcting offenses'
end

context 'for a constructed Array without a block' do
let(:source_rhs) { 'Array.new(3)' }

include_examples 'detecting and correcting offenses'
end

%w(i I w W).each do |literal_type|
context "for a %#{literal_type} literal" do
let(:source_rhs) { "%#{literal_type}{1 2 3}" }

include_examples 'detecting and correcting offenses'
end
end
end

context 'without splat' do
context 'for an array' do
let(:source_rhs) { '[]' }

include_examples 'detect and correct no offenses'
end

context 'for a symbol' do
let(:source_rhs) { ':a' }

include_examples 'detect and correct no offenses'
end

context 'for a variable' do
let(:source_rhs) { 'foo' }

include_examples 'detect and correct no offenses'
end
end
end
end
end
end

0 comments on commit b4eaaa6

Please sign in to comment.