Skip to content

Commit

Permalink
Updated Gemspec/RequiredRubyVersion register an offense when set to…
Browse files Browse the repository at this point in the history
… blank values.
  • Loading branch information
dvandersluis committed Oct 4, 2021
1 parent 1bed3ec commit 41b59f8
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 33 deletions.
1 change: 1 addition & 0 deletions changelog/change_updated_gemspecrequiredrubyversion_to.md
@@ -0,0 +1 @@
* [#10157](https://github.com/rubocop/rubocop/pull/10157): Updated `Gemspec/RequiredRubyVersion` handle being set to blank values. ([@dvandersluis][])
2 changes: 1 addition & 1 deletion config/default.yml
Expand Up @@ -262,7 +262,7 @@ Gemspec/RequiredRubyVersion:
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'
VersionChanged: '<<next>>'
Include:
- '**/*.gemspec'

Expand Down
53 changes: 30 additions & 23 deletions lib/rubocop/cop/gemspec/required_ruby_version.rb
Expand Up @@ -3,10 +3,11 @@
module RuboCop
module Cop
module Gemspec
# 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.
# Checks that `required_ruby_version` in a gemspec file is set to a valid
# value (non-blank) and matches `TargetRubyVersion` as set in RuboCop's
# configuration for the gem.
#
# This ensures that RuboCop is using the same Ruby version as the gem.
#
# @example
# # When `TargetRubyVersion` of .rubocop.yml is `2.5`.
Expand All @@ -26,6 +27,11 @@ module Gemspec
# spec.required_ruby_version = '>= 2.6.0'
# end
#
# # bad
# Gem::Specification.new do |spec|
# spec.required_ruby_version = ''
# end
#
# # good
# Gem::Specification.new do |spec|
# spec.required_ruby_version = '>= 2.5.0'
Expand All @@ -49,15 +55,15 @@ module Gemspec
class RequiredRubyVersion < Base
include RangeHelp

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

# @!method required_ruby_version(node)
def_node_search :required_ruby_version, <<~PATTERN
(send _ :required_ruby_version= $_)
# @!method required_ruby_version?(node)
def_node_search :required_ruby_version?, <<~PATTERN
(send _ :required_ruby_version= _)
PATTERN

# @!method defined_ruby_version(node)
Expand All @@ -66,27 +72,28 @@ class RequiredRubyVersion < Base
(send (const (const nil? :Gem) :Requirement) :new $(str _))}
PATTERN

# rubocop:disable Metrics/AbcSize
def on_new_investigation
version_def = required_ruby_version(processed_source.ast).first
add_global_offense(MISSING_MSG) unless required_ruby_version?(processed_source.ast)
end

if version_def
ruby_version = extract_ruby_version(defined_ruby_version(version_def))
return if !ruby_version || ruby_version == target_ruby_version.to_s
def on_send(node)
version_def = node.first_argument
return if dynamic_version?(version_def)

add_offense(
version_def.loc.expression,
message: not_equal_message(ruby_version, target_ruby_version)
)
else
range = source_range(processed_source.buffer, 1, 0)
add_offense(range, message: MISSING_MSG)
end
ruby_version = extract_ruby_version(defined_ruby_version(version_def))
return if ruby_version == target_ruby_version.to_s

add_offense(version_def, message: not_equal_message(ruby_version, target_ruby_version))
end
# rubocop:enable Metrics/AbcSize

private

def dynamic_version?(node)
(node.send_type? && !node.receiver) ||
node.variable? ||
node.each_descendant(:send, *RuboCop::AST::Node::VARIABLES).any?
end

def extract_ruby_version(required_ruby_version)
return unless required_ruby_version

Expand Down
10 changes: 10 additions & 0 deletions spec/rubocop/cop/gemspec/duplicated_assignment_spec.rb
Expand Up @@ -33,6 +33,16 @@
RUBY
end

it 'registers an offense when using `required_ruby_version=` twice' do
expect_offense(<<~RUBY)
::Gem::Specification.new do |spec|
spec.required_ruby_version = '2.5'
spec.required_ruby_version = '2.6'
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `required_ruby_version=` method calls already given on line 2 of the gemspec.
end
RUBY
end

it 'does not register an offense when using `<<` twice' do
expect_no_offenses(<<~RUBY)
Gem::Specification.new do |spec|
Expand Down
36 changes: 27 additions & 9 deletions spec/rubocop/cop/gemspec/required_ruby_version_spec.rb
Expand Up @@ -6,7 +6,7 @@
expect_offense(<<~RUBY, '/path/to/foo.gemspec')
Gem::Specification.new do |spec|
spec.required_ruby_version = '>= 2.6.0'
^^^^^^^^^^ `required_ruby_version` (2.6, declared in foo.gemspec) and `TargetRubyVersion` (2.7, which may be specified in .rubocop.yml) should be equal.
^^^^^^^^^^ `required_ruby_version` and `TargetRubyVersion` (2.7, which may be specified in .rubocop.yml) should be equal.
end
RUBY
end
Expand All @@ -15,7 +15,7 @@
expect_offense(<<~RUBY, '/path/to/foo.gemspec')
Gem::Specification.new do |spec|
spec.required_ruby_version = '~> 2.6.0'
^^^^^^^^^^ `required_ruby_version` (2.6, declared in foo.gemspec) and `TargetRubyVersion` (2.7, which may be specified in .rubocop.yml) should be equal.
^^^^^^^^^^ `required_ruby_version` and `TargetRubyVersion` (2.7, which may be specified in .rubocop.yml) should be equal.
end
RUBY
end
Expand All @@ -24,7 +24,7 @@
expect_offense(<<~RUBY, '/path/to/foo.gemspec')
Gem::Specification.new do |spec|
spec.required_ruby_version = ['>= 2.6.0', '< 3.0.0']
^^^^^^^^^^^^^^^^^^^^^^^ `required_ruby_version` (2.6, declared in foo.gemspec) and `TargetRubyVersion` (2.7, which may be specified in .rubocop.yml) should be equal.
^^^^^^^^^^^^^^^^^^^^^^^ `required_ruby_version` and `TargetRubyVersion` (2.7, which may be specified in .rubocop.yml) should be equal.
end
RUBY
end
Expand All @@ -33,7 +33,7 @@
expect_offense(<<~RUBY, '/path/to/foo.gemspec')
Gem::Specification.new do |spec|
spec.required_ruby_version = Gem::Requirement.new(">= 2.6.0")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `required_ruby_version` (2.6, declared in foo.gemspec) and `TargetRubyVersion` (2.7, which may be specified in .rubocop.yml) should be equal.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `required_ruby_version` and `TargetRubyVersion` (2.7, which may be specified in .rubocop.yml) should be equal.
end
RUBY
end
Expand Down Expand Up @@ -67,7 +67,7 @@
expect_offense(<<~RUBY, '/path/to/bar.gemspec')
Gem::Specification.new do |spec|
spec.required_ruby_version = '>= 2.6.0'
^^^^^^^^^^ `required_ruby_version` (2.6, declared in bar.gemspec) and `TargetRubyVersion` (2.5, which may be specified in .rubocop.yml) should be equal.
^^^^^^^^^^ `required_ruby_version` and `TargetRubyVersion` (2.5, which may be specified in .rubocop.yml) should be equal.
end
RUBY
end
Expand All @@ -76,7 +76,7 @@
expect_offense(<<~RUBY, '/path/to/bar.gemspec')
Gem::Specification.new do |spec|
spec.required_ruby_version = '~> 2.6.0'
^^^^^^^^^^ `required_ruby_version` (2.6, declared in bar.gemspec) and `TargetRubyVersion` (2.5, which may be specified in .rubocop.yml) should be equal.
^^^^^^^^^^ `required_ruby_version` and `TargetRubyVersion` (2.5, which may be specified in .rubocop.yml) should be equal.
end
RUBY
end
Expand Down Expand Up @@ -131,7 +131,7 @@
expect_offense(<<~RUBY, 'bar.gemspec')
Gem::Specification.new do |spec|
spec.required_ruby_version = '>= 2'
^^^^^^ `required_ruby_version` (2, declared in bar.gemspec) and `TargetRubyVersion` (2.6, which may be specified in .rubocop.yml) should be equal.
^^^^^^ `required_ruby_version` and `TargetRubyVersion` (2.6, which may be specified in .rubocop.yml) should be equal.
end
RUBY
end
Expand All @@ -141,7 +141,7 @@
expect_offense(<<~RUBY, 'bar.gemspec')
Gem::Specification.new do |spec|
spec.required_ruby_version = '~> 2'
^^^^^^ `required_ruby_version` (2, declared in bar.gemspec) and `TargetRubyVersion` (2.6, which may be specified in .rubocop.yml) should be equal.
^^^^^^ `required_ruby_version` and `TargetRubyVersion` (2.6, which may be specified in .rubocop.yml) should be equal.
end
RUBY
end
Expand All @@ -150,7 +150,25 @@
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.
^{} `required_ruby_version` should be specified.
end
RUBY
end

it 'registers an offense when `required_ruby_version` is blank' do
expect_offense(<<~RUBY, 'bar.gemspec')
Gem::Specification.new do |spec|
spec.required_ruby_version = ''
^^ `required_ruby_version` and `TargetRubyVersion` (2.5, which may be specified in .rubocop.yml) should be equal.
end
RUBY
end

it 'registers an offense when `required_ruby_version` is an empty array' do
expect_offense(<<~RUBY, 'bar.gemspec')
Gem::Specification.new do |spec|
spec.required_ruby_version = []
^^ `required_ruby_version` and `TargetRubyVersion` (2.5, which may be specified in .rubocop.yml) should be equal.
end
RUBY
end
Expand Down

0 comments on commit 41b59f8

Please sign in to comment.