Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated Gemspec/RequiredRubyVersion to handle blank values #10157

Merged
merged 1 commit into from Oct 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
dvandersluis marked this conversation as resolved.
Show resolved Hide resolved
#
# # 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