From e05c000f14e215183c914c7f119202f96da12179 Mon Sep 17 00:00:00 2001 From: fatkodima Date: Sun, 19 Jul 2020 16:42:03 +0300 Subject: [PATCH] Enhance `Gemspec/RequiredRubyVersion` cop with check that `required_ruby_version` is specified --- CHANGELOG.md | 1 + config/default.yml | 3 +- docs/modules/ROOT/pages/cops_gemspec.adoc | 11 +++-- .../cop/gemspec/required_ruby_version.rb | 43 ++++++++++++++----- .../cop/gemspec/required_ruby_version_spec.rb | 8 ++++ 5 files changed, 51 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1aae2bd3f60..a3866daa55f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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][]) diff --git a/config/default.yml b/config/default.yml index 3143f5a13cd..0e951f03bf9 100644 --- a/config/default.yml +++ b/config/default.yml @@ -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' diff --git a/docs/modules/ROOT/pages/cops_gemspec.adoc b/docs/modules/ROOT/pages/cops_gemspec.adoc index 619828ca092..9c6a8f46d96 100644 --- a/docs/modules/ROOT/pages/cops_gemspec.adoc +++ b/docs/modules/ROOT/pages/cops_gemspec.adoc @@ -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. @@ -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' diff --git a/lib/rubocop/cop/gemspec/required_ruby_version.rb b/lib/rubocop/cop/gemspec/required_ruby_version.rb index c399bb4ab53..aeed941b855 100644 --- a/lib/rubocop/cop/gemspec/required_ruby_version.rb +++ b/lib/rubocop/cop/gemspec/required_ruby_version.rb @@ -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. # @@ -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 # @@ -41,17 +46,29 @@ module Gemspec # spec.required_ruby_version = '~> 2.5' # end class RequiredRubyVersion < Cop - MSG = '`required_ruby_version` (%s, ' \ - 'declared in %s) and `TargetRubyVersion` ' \ - '(%s, which may be specified in ' \ - '.rubocop.yml) should be equal.' + include RangeHelp + + NOT_EQUAL_MSG = '`required_ruby_version` (%s, ' \ + 'declared in %s) and `TargetRubyVersion` ' \ + '(%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 @@ -59,10 +76,14 @@ def investigate(processed_source) 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 @@ -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 diff --git a/spec/rubocop/cop/gemspec/required_ruby_version_spec.rb b/spec/rubocop/cop/gemspec/required_ruby_version_spec.rb index 44d7d4abe5f..789df4a1f07 100644 --- a/spec/rubocop/cop/gemspec/required_ruby_version_spec.rb +++ b/spec/rubocop/cop/gemspec/required_ruby_version_spec.rb @@ -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