Skip to content

Commit

Permalink
Enhance Gemspec/RequiredRubyVersion cop with check that `required_r…
Browse files Browse the repository at this point in the history
…uby_version` is specified
  • Loading branch information
fatkodima committed Jul 23, 2020
1 parent 7795df4 commit e05c000
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 15 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -5,6 +5,7 @@
### New features

* [#8322](https://github.com/rubocop-hq/rubocop/pull/8322): Support autocorrect for `Style/CaseEquality` cop. ([@fatkodima][])
* [#7876](https://github.com/rubocop-hq/rubocop/issues/7876): Enhance `Gemspec/RequiredRubyVersion` cop with check that `required_ruby_version` is specified. ([@fatkodima][])
* [#8376](https://github.com/rubocop-hq/rubocop/pull/8376): Add new `Lint/MissingSuper` cop. ([@fatkodima][])
* [#8339](https://github.com/rubocop-hq/rubocop/pull/8339): Add `Config#for_badge` as an efficient way to get a cop's config merged with its department's. ([@marcandre][])
* [#5067](https://github.com/rubocop-hq/rubocop/issues/5067): Add new `Style/StringConcatenation` cop. ([@fatkodima][])
Expand Down
3 changes: 2 additions & 1 deletion config/default.yml
Expand Up @@ -210,9 +210,10 @@ Gemspec/OrderedDependencies:
- '**/*.gemspec'

Gemspec/RequiredRubyVersion:
Description: 'Checks that `required_ruby_version` of gemspec and `TargetRubyVersion` of .rubocop.yml are equal.'
Description: 'Checks that `required_ruby_version` of gemspec is specified and equal to `TargetRubyVersion` of .rubocop.yml.'
Enabled: true
VersionAdded: '0.52'
VersionChanged: '0.89'
Include:
- '**/*.gemspec'

Expand Down
11 changes: 8 additions & 3 deletions docs/modules/ROOT/pages/cops_gemspec.adoc
Expand Up @@ -149,11 +149,11 @@ spec.add_dependency 'rspec'
| Yes
| No
| 0.52
| -
| 0.89
|===

Checks that `required_ruby_version` of gemspec and `TargetRubyVersion`
of .rubocop.yml are equal.
Checks that `required_ruby_version` of gemspec is specified and
equal to `TargetRubyVersion` of .rubocop.yml.
Thereby, RuboCop to perform static analysis working on the version
required by gemspec.

Expand All @@ -163,6 +163,11 @@ required by gemspec.
----
# When `TargetRubyVersion` of .rubocop.yml is `2.5`.
# bad
Gem::Specification.new do |spec|
# no `required_ruby_version` specified
end
# bad
Gem::Specification.new do |spec|
spec.required_ruby_version = '>= 2.4.0'
Expand Down
43 changes: 32 additions & 11 deletions lib/rubocop/cop/gemspec/required_ruby_version.rb
Expand Up @@ -3,8 +3,8 @@
module RuboCop
module Cop
module Gemspec
# Checks that `required_ruby_version` of gemspec and `TargetRubyVersion`
# of .rubocop.yml are equal.
# Checks that `required_ruby_version` of gemspec is specified and
# equal to `TargetRubyVersion` of .rubocop.yml.
# Thereby, RuboCop to perform static analysis working on the version
# required by gemspec.
#
Expand All @@ -13,6 +13,11 @@ module Gemspec
#
# # bad
# Gem::Specification.new do |spec|
# # no `required_ruby_version` specified
# end
#
# # bad
# Gem::Specification.new do |spec|
# spec.required_ruby_version = '>= 2.4.0'
# end
#
Expand Down Expand Up @@ -41,28 +46,44 @@ module Gemspec
# spec.required_ruby_version = '~> 2.5'
# end
class RequiredRubyVersion < Cop
MSG = '`required_ruby_version` (%<required_ruby_version>s, ' \
'declared in %<gemspec_filename>s) and `TargetRubyVersion` ' \
'(%<target_ruby_version>s, which may be specified in ' \
'.rubocop.yml) should be equal.'
include RangeHelp

NOT_EQUAL_MSG = '`required_ruby_version` (%<required_ruby_version>s, ' \
'declared in %<gemspec_filename>s) and `TargetRubyVersion` ' \
'(%<target_ruby_version>s, which may be specified in ' \
'.rubocop.yml) should be equal.'
MISSING_MSG = '`required_ruby_version` should be specified.'

def_node_search :required_ruby_version, <<~PATTERN
(send _ :required_ruby_version= ${(str _) (array (str _))})
(send _ :required_ruby_version= $_)
PATTERN

def_node_matcher :string_version?, <<~PATTERN
{(str _) (array (str _))}
PATTERN

# rubocop:disable Metrics/AbcSize
def investigate(processed_source)
required_ruby_version(processed_source.ast) do |version|
version = required_ruby_version(processed_source.ast).first

if version
return unless string_version?(version)

ruby_version = extract_ruby_version(version)

return if ruby_version == target_ruby_version.to_s

add_offense(
processed_source.ast,
location: version.loc.expression,
message: message(ruby_version, target_ruby_version)
message: not_equal_message(ruby_version, target_ruby_version)
)
else
range = source_range(processed_source.buffer, 1, 0)
add_offense(nil, location: range, message: MISSING_MSG)
end
end
# rubocop:enable Metrics/AbcSize

private

Expand All @@ -76,9 +97,9 @@ def extract_ruby_version(required_ruby_version)
required_ruby_version.str_content.scan(/\d/).first(2).join('.')
end

def message(required_ruby_version, target_ruby_version)
def not_equal_message(required_ruby_version, target_ruby_version)
format(
MSG,
NOT_EQUAL_MSG,
required_ruby_version: required_ruby_version,
gemspec_filename: File.basename(processed_source.file_path),
target_ruby_version: target_ruby_version
Expand Down
8 changes: 8 additions & 0 deletions spec/rubocop/cop/gemspec/required_ruby_version_spec.rb
Expand Up @@ -128,4 +128,12 @@
RUBY
end
end

it 'registers an offense when `required_ruby_version` is not specified' do
expect_offense(<<~RUBY, '/path/to/foo.gemspec')
Gem::Specification.new do |spec|
^ `required_ruby_version` should be specified.
end
RUBY
end
end

0 comments on commit e05c000

Please sign in to comment.