-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[#3600] Add new
Bundler/DuplicatedGem
cop
The DuplicatedGem cop checks for duplicate gem entries in Gemfiles.
- Loading branch information
Showing
9 changed files
with
202 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
# frozen_string_literal: true | ||
|
||
module RuboCop | ||
module Cop | ||
module Bundler | ||
# A Gem's requirements should be listed only once in a Gemfile. | ||
# @example | ||
# # bad | ||
# gem 'rubocop' | ||
# gem 'rubocop' | ||
# | ||
# # bad | ||
# group :development do | ||
# gem 'rubocop' | ||
# end | ||
# | ||
# group :test do | ||
# gem 'rubocop' | ||
# end | ||
# | ||
# # good | ||
# group :development, :test do | ||
# gem 'rubocop' | ||
# end | ||
# | ||
# # good | ||
# gem 'rubocop', groups: [:development, :test] | ||
class DuplicatedGem < Cop | ||
MSG = 'Gem `%s` requirements already given on line %d ' \ | ||
'of the Gemfile.'.freeze | ||
|
||
def investigate(processed_source) | ||
return unless processed_source.ast | ||
|
||
duplicated_gem_nodes.each do |nodes| | ||
nodes[1..-1].each do |node| | ||
offense( | ||
node, | ||
node.method_args.first.to_a.first, | ||
nodes.first.loc.line | ||
) | ||
end | ||
end | ||
end | ||
|
||
private | ||
|
||
def_node_search :gem_declarations, '(send nil :gem str ...)' | ||
|
||
def duplicated_gem_nodes | ||
gem_declarations(processed_source.ast) | ||
.group_by { |e| e.method_args.first } | ||
.keep_if { |_, nodes| nodes.length > 1 } | ||
.values | ||
end | ||
|
||
def offense(node, gem_name, line_of_first_occurence) | ||
line_range = node.loc.column...node.loc.last_column | ||
|
||
add_offense( | ||
node, | ||
source_range(processed_source.buffer, node.loc.line, line_range), | ||
format(MSG, gem_name, line_of_first_occurence) | ||
) | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# Bundler | ||
|
||
## Bundler/DuplicatedGem | ||
|
||
Enabled by default | Supports autocorrection | ||
--- | --- | ||
Enabled | No | ||
|
||
This cop checks for duplicate gem entries in Gemfiles. Bundler currently | ||
only prints a warning (unless there is a requirements conflict). | ||
|
||
### Important attributes | ||
|
||
Attribute | Value | | ||
--- | --- | | ||
Include | \*\*/Gemfile, \*\*/gems.rb | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'spec_helper' | ||
|
||
describe RuboCop::Cop::Bundler::DuplicatedGem, :config do | ||
let(:cop_config) { { 'Include' => ['**/Gemfile'] } } | ||
subject(:cop) { described_class.new(config) } | ||
|
||
context 'when investigating Ruby files' do | ||
let(:source) { <<-END } | ||
# cop will not read these contents | ||
gem('rubocop') | ||
gem('rubocop') | ||
END | ||
|
||
it 'does not register any offenses' do | ||
inspect_source_file(cop, source) | ||
expect(cop.offenses).to be_empty | ||
end | ||
end | ||
|
||
context 'when investigating Gemfiles' do | ||
context 'and the file is empty' do | ||
let(:source) { '' } | ||
|
||
it 'does not raise an error' do | ||
expect { inspect_source(cop, source, 'gems.rb') }.not_to raise_error | ||
end | ||
|
||
it 'does not register any offenses' do | ||
expect(cop.offenses).to be_empty | ||
end | ||
end | ||
|
||
context 'and no duplicate gems are present' do | ||
let(:source) { <<-GEM } | ||
gem 'rubocop' | ||
gem 'flog' | ||
GEM | ||
|
||
it 'does not register any offenses' do | ||
inspect_gemfile(cop, source) | ||
expect(cop.offenses).to be_empty | ||
end | ||
end | ||
|
||
context 'and a gem is duplicated in default group' do | ||
let(:source) { <<-GEM } | ||
source 'https://rubygems.org' | ||
gem 'rubocop' | ||
gem 'rubocop' | ||
GEM | ||
|
||
it 'registers an offense' do | ||
inspect_gemfile(cop, source) | ||
expect(cop.offenses.size).to eq(1) | ||
end | ||
|
||
it "references gem's first occurance in message" do | ||
inspect_gemfile(cop, source) | ||
expect(cop.offenses.first.message).to include('2') | ||
end | ||
|
||
it 'highlights the duplicate gem' do | ||
inspect_gemfile(cop, source) | ||
expect(cop.highlights).to eq(["gem 'rubocop'"]) | ||
end | ||
end | ||
|
||
context 'and a duplicated gem is in a git/path/group/platforms block' do | ||
let(:source) { <<-GEM } | ||
gem 'rubocop' | ||
group :development do | ||
gem 'rubocop', path: '/path/to/gem' | ||
end | ||
GEM | ||
|
||
it 'registers an offense' do | ||
inspect_gemfile(cop, source) | ||
expect(cop.offenses.size).to eq(1) | ||
end | ||
|
||
it 'highlights the duplicate gem' do | ||
inspect_gemfile(cop, source) | ||
expect(cop.highlights).to eq(["gem 'rubocop', path: '/path/to/gem'"]) | ||
end | ||
end | ||
end | ||
end |