Skip to content

Commit

Permalink
[Fix rubocop#9328] Recognize shareable_constant_value magic comment
Browse files Browse the repository at this point in the history
  • Loading branch information
caalberts committed Feb 19, 2021
1 parent 8bd7679 commit 40e63ec
Show file tree
Hide file tree
Showing 7 changed files with 339 additions and 15 deletions.
@@ -0,0 +1 @@
* [#9328](https://github.com/rubocop-hq/rubocop/issues/9328): Recognize shareable_constant_value magic comment. ([@caalberts][])
1 change: 1 addition & 0 deletions lib/rubocop.rb
Expand Up @@ -113,6 +113,7 @@
require_relative 'rubocop/cop/mixin/rational_literal'
require_relative 'rubocop/cop/mixin/rescue_node'
require_relative 'rubocop/cop/mixin/safe_assignment'
require_relative 'rubocop/cop/mixin/shareable_constant_value'
require_relative 'rubocop/cop/mixin/space_after_punctuation'
require_relative 'rubocop/cop/mixin/space_before_punctuation'
require_relative 'rubocop/cop/mixin/surrounding_space'
Expand Down
34 changes: 34 additions & 0 deletions lib/rubocop/cop/mixin/shareable_constant_value.rb
@@ -0,0 +1,34 @@
# frozen_string_literal: true

module RuboCop
module Cop
# Common functionality for dealing with shareable constant value.
module ShareableConstantValue
module_function

LITERAL = :literal
EXPERIMENTAL_ANYTHING = :experimental_everything

def shareable_constant_value?
target_ruby_version >= 3.0 && shareable_constant_value_comment_exists?
end

def shareable_constant_value_comment_exists?
leading_comments.any? do |line|
shareable_constant_value = MagicComment.parse(line).shareable_constant_value

[LITERAL, EXPERIMENTAL_ANYTHING].include?(shareable_constant_value)
end
end

private

def leading_comments
processed_source
.tokens
.take_while(&:comment?)
.map(&:text)
end
end
end
end
4 changes: 4 additions & 0 deletions lib/rubocop/cop/style/mutable_constant.rb
Expand Up @@ -54,6 +54,7 @@ module Style
# end.freeze
class MutableConstant < Base
include FrozenStringLiteral
include ShareableConstantValue
include ConfigurableEnforcedStyle
extend AutoCorrector

Expand Down Expand Up @@ -83,6 +84,7 @@ def on_assignment(value)
end

def strict_check(value)
return if shareable_constant_value?
return if immutable_literal?(value)
return if operation_produces_immutable_object?(value)
return if frozen_string_literal?(value)
Expand All @@ -93,6 +95,8 @@ def strict_check(value)
end

def check(value)
return if shareable_constant_value?

range_enclosed_in_parentheses = range_enclosed_in_parentheses?(value)

return unless mutable_literal?(value) ||
Expand Down
10 changes: 8 additions & 2 deletions lib/rubocop/magic_comment.rb
Expand Up @@ -84,7 +84,13 @@ def frozen_string_literal
#
# @return [String] for shareable_constant_value config
def shareable_constant_value
extract_shareable_constant_value
return unless (setting = extract_shareable_constant_value)

case setting
when 'none' then nil
else
setting.to_sym
end
end

def encoding_specified?
Expand Down Expand Up @@ -166,7 +172,7 @@ def extract_frozen_string_literal
end

def extract_shareable_constant_value
match('shareable[_-]constant[_-]values')
match('shareable[_-]constant[_-]value')
end
end

Expand Down
250 changes: 250 additions & 0 deletions spec/rubocop/cop/style/mutable_constant_spec.rb
Expand Up @@ -157,6 +157,61 @@
RUBY
end
end

context 'when using shareable_constant_value: literal' do
let(:prefix) { '# shareable_constant_value: literal' }

it_behaves_like 'immutable objects', '[1, 2, 3]'
it_behaves_like 'immutable objects', '%w(a b c)'
it_behaves_like 'immutable objects', '{ a: 1, b: 2 }'
it_behaves_like 'immutable objects', "'str'"
it_behaves_like 'immutable objects', '"top#{1 + 2}"'
it_behaves_like 'immutable objects', '1'
it_behaves_like 'immutable objects', '1.5'
it_behaves_like 'immutable objects', ':sym'
it_behaves_like 'immutable objects', 'FOO + BAR'
it_behaves_like 'immutable objects', 'FOO - BAR'
it_behaves_like 'immutable objects', "'foo' + 'bar'"
it_behaves_like 'immutable objects', "ENV['foo']"
it_behaves_like 'immutable objects', "::ENV['foo']"
end

context 'when using shareable_constant_value: experimental_everything' do
let(:prefix) { '# shareable_constant_value: experimental_everything' }

it_behaves_like 'immutable objects', '[1, 2, 3]'
it_behaves_like 'immutable objects', '%w(a b c)'
it_behaves_like 'immutable objects', '{ a: 1, b: 2 }'
it_behaves_like 'immutable objects', "'str'"
it_behaves_like 'immutable objects', '"top#{1 + 2}"'
it_behaves_like 'immutable objects', '1'
it_behaves_like 'immutable objects', '1.5'
it_behaves_like 'immutable objects', ':sym'
it_behaves_like 'immutable objects', 'FOO + BAR'
it_behaves_like 'immutable objects', 'FOO - BAR'
it_behaves_like 'immutable objects', "'foo' + 'bar'"
it_behaves_like 'immutable objects', "ENV['foo']"
it_behaves_like 'immutable objects', "::ENV['foo']"
end

context 'when using shareable_constant_value: none' do
let(:prefix) { '# shareable_constant_value: none' }

it_behaves_like 'mutable objects', '[1, 2, 3]'
it_behaves_like 'mutable objects', '%w(a b c)'
it_behaves_like 'mutable objects', '{ a: 1, b: 2 }'
it_behaves_like 'mutable objects', "'str'"
it_behaves_like 'mutable objects', '"top#{1 + 2}"'

it_behaves_like 'immutable objects', '1'
it_behaves_like 'immutable objects', '1.5'
it_behaves_like 'immutable objects', ':sym'
it_behaves_like 'immutable objects', 'FOO + BAR'
it_behaves_like 'immutable objects', 'FOO - BAR'
it_behaves_like 'immutable objects', "'foo' + 'bar'"
it_behaves_like 'immutable objects', "ENV['foo']"
it_behaves_like 'immutable objects', "::ENV['foo']"
end
end

context 'Ruby 2.7 or lower', :ruby27 do
Expand Down Expand Up @@ -220,6 +275,44 @@
RUBY
end
end

context 'when using shareable_constant_value: literal' do
let(:prefix) { '# shareable_constant_value: literal' }

it_behaves_like 'mutable objects', '[1, 2, 3]'
it_behaves_like 'mutable objects', '%w(a b c)'
it_behaves_like 'mutable objects', '{ a: 1, b: 2 }'
it_behaves_like 'mutable objects', "'str'"
it_behaves_like 'mutable objects', '"top#{1 + 2}"'

it_behaves_like 'immutable objects', '1'
it_behaves_like 'immutable objects', '1.5'
it_behaves_like 'immutable objects', ':sym'
it_behaves_like 'immutable objects', 'FOO + BAR'
it_behaves_like 'immutable objects', 'FOO - BAR'
it_behaves_like 'immutable objects', "'foo' + 'bar'"
it_behaves_like 'immutable objects', "ENV['foo']"
it_behaves_like 'immutable objects', "::ENV['foo']"
end

context 'when using shareable_constant_value: experimental_everything' do
let(:prefix) { '# shareable_constant_value: experimental_everything' }

it_behaves_like 'mutable objects', '[1, 2, 3]'
it_behaves_like 'mutable objects', '%w(a b c)'
it_behaves_like 'mutable objects', '{ a: 1, b: 2 }'
it_behaves_like 'mutable objects', "'str'"
it_behaves_like 'mutable objects', '"top#{1 + 2}"'

it_behaves_like 'immutable objects', '1'
it_behaves_like 'immutable objects', '1.5'
it_behaves_like 'immutable objects', ':sym'
it_behaves_like 'immutable objects', 'FOO + BAR'
it_behaves_like 'immutable objects', 'FOO - BAR'
it_behaves_like 'immutable objects', "'foo' + 'bar'"
it_behaves_like 'immutable objects', "ENV['foo']"
it_behaves_like 'immutable objects', "::ENV['foo']"
end
end

context 'when the constant is a frozen string literal' do
Expand Down Expand Up @@ -501,5 +594,162 @@ def assignment?

it_behaves_like 'mutable objects', '"#{a}"'
end

context 'Ruby 3.0 or higher', :ruby30 do
context 'when using shareable_constant_value: literal' do
let(:prefix) { '# shareable_constant_value: literal' }

it_behaves_like 'immutable objects', '[1, 2, 3]'
it_behaves_like 'immutable objects', '%w(a b c)'
it_behaves_like 'immutable objects', '{ a: 1, b: 2 }'
it_behaves_like 'immutable objects', "'str'"
it_behaves_like 'immutable objects', '"top#{1 + 2}"'
it_behaves_like 'immutable objects', 'Something.new'
it_behaves_like 'immutable objects', '1'
it_behaves_like 'immutable objects', '1.5'
it_behaves_like 'immutable objects', ':sym'
it_behaves_like 'immutable objects', "ENV['foo']"
it_behaves_like 'immutable objects', "::ENV['foo']"
it_behaves_like 'immutable objects', 'OTHER_CONST'
it_behaves_like 'immutable objects', '::OTHER_CONST'
it_behaves_like 'immutable objects', 'Namespace::OTHER_CONST'
it_behaves_like 'immutable objects', '::Namespace::OTHER_CONST'
it_behaves_like 'immutable objects', 'Struct.new'
it_behaves_like 'immutable objects', '::Struct.new'
it_behaves_like 'immutable objects', 'Struct.new(:a, :b)'
it_behaves_like 'immutable objects', <<~RUBY
Struct.new(:node) do
def assignment?
true
end
end
RUBY
end

context 'when using shareable_constant_value: experimental_everything' do
let(:prefix) { '# shareable_constant_value: experimental_everything' }

it_behaves_like 'immutable objects', '[1, 2, 3]'
it_behaves_like 'immutable objects', '%w(a b c)'
it_behaves_like 'immutable objects', '{ a: 1, b: 2 }'
it_behaves_like 'immutable objects', "'str'"
it_behaves_like 'immutable objects', '"top#{1 + 2}"'
it_behaves_like 'immutable objects', 'Something.new'
it_behaves_like 'immutable objects', '1'
it_behaves_like 'immutable objects', '1.5'
it_behaves_like 'immutable objects', ':sym'
it_behaves_like 'immutable objects', "ENV['foo']"
it_behaves_like 'immutable objects', "::ENV['foo']"
it_behaves_like 'immutable objects', 'OTHER_CONST'
it_behaves_like 'immutable objects', '::OTHER_CONST'
it_behaves_like 'immutable objects', 'Namespace::OTHER_CONST'
it_behaves_like 'immutable objects', '::Namespace::OTHER_CONST'
it_behaves_like 'immutable objects', 'Struct.new'
it_behaves_like 'immutable objects', '::Struct.new'
it_behaves_like 'immutable objects', 'Struct.new(:a, :b)'
it_behaves_like 'immutable objects', <<~RUBY
Struct.new(:node) do
def assignment?
true
end
end
RUBY
end

context 'when using shareable_constant_value: none' do
let(:prefix) { '# shareable_constant_value: none' }

it_behaves_like 'mutable objects', '[1, 2, 3]'
it_behaves_like 'mutable objects', '%w(a b c)'
it_behaves_like 'mutable objects', '{ a: 1, b: 2 }'
it_behaves_like 'mutable objects', "'str'"
it_behaves_like 'mutable objects', '"top#{1 + 2}"'
it_behaves_like 'mutable objects', 'Something.new'

it_behaves_like 'immutable objects', '1'
it_behaves_like 'immutable objects', '1.5'
it_behaves_like 'immutable objects', ':sym'
it_behaves_like 'immutable objects', "ENV['foo']"
it_behaves_like 'immutable objects', "::ENV['foo']"
it_behaves_like 'immutable objects', 'OTHER_CONST'
it_behaves_like 'immutable objects', '::OTHER_CONST'
it_behaves_like 'immutable objects', 'Namespace::OTHER_CONST'
it_behaves_like 'immutable objects', '::Namespace::OTHER_CONST'
it_behaves_like 'immutable objects', 'Struct.new'
it_behaves_like 'immutable objects', '::Struct.new'
it_behaves_like 'immutable objects', 'Struct.new(:a, :b)'
it_behaves_like 'immutable objects', <<~RUBY
Struct.new(:node) do
def assignment?
true
end
end
RUBY
end
end

context 'Ruby 2.7 or lower', :ruby27 do
context 'when using shareable_constant_value: literal' do
let(:prefix) { '# shareable_constant_value: literal' }

it_behaves_like 'mutable objects', '[1, 2, 3]'
it_behaves_like 'mutable objects', '%w(a b c)'
it_behaves_like 'mutable objects', '{ a: 1, b: 2 }'
it_behaves_like 'mutable objects', "'str'"
it_behaves_like 'mutable objects', '"top#{1 + 2}"'
it_behaves_like 'mutable objects', 'Something.new'

it_behaves_like 'immutable objects', '1'
it_behaves_like 'immutable objects', '1.5'
it_behaves_like 'immutable objects', ':sym'
it_behaves_like 'immutable objects', "ENV['foo']"
it_behaves_like 'immutable objects', "::ENV['foo']"
it_behaves_like 'immutable objects', 'OTHER_CONST'
it_behaves_like 'immutable objects', '::OTHER_CONST'
it_behaves_like 'immutable objects', 'Namespace::OTHER_CONST'
it_behaves_like 'immutable objects', '::Namespace::OTHER_CONST'
it_behaves_like 'immutable objects', 'Struct.new'
it_behaves_like 'immutable objects', '::Struct.new'
it_behaves_like 'immutable objects', 'Struct.new(:a, :b)'
it_behaves_like 'immutable objects', <<~RUBY
Struct.new(:node) do
def assignment?
true
end
end
RUBY
end

context 'when using shareable_constant_value: experimental_everything' do
let(:prefix) { '# shareable_constant_value: experimental_everything' }

it_behaves_like 'mutable objects', '[1, 2, 3]'
it_behaves_like 'mutable objects', '%w(a b c)'
it_behaves_like 'mutable objects', '{ a: 1, b: 2 }'
it_behaves_like 'mutable objects', "'str'"
it_behaves_like 'mutable objects', '"top#{1 + 2}"'
it_behaves_like 'mutable objects', 'Something.new'

it_behaves_like 'immutable objects', '1'
it_behaves_like 'immutable objects', '1.5'
it_behaves_like 'immutable objects', ':sym'
it_behaves_like 'immutable objects', "ENV['foo']"
it_behaves_like 'immutable objects', "::ENV['foo']"
it_behaves_like 'immutable objects', 'OTHER_CONST'
it_behaves_like 'immutable objects', '::OTHER_CONST'
it_behaves_like 'immutable objects', 'Namespace::OTHER_CONST'
it_behaves_like 'immutable objects', '::Namespace::OTHER_CONST'
it_behaves_like 'immutable objects', 'Struct.new'
it_behaves_like 'immutable objects', '::Struct.new'
it_behaves_like 'immutable objects', 'Struct.new(:a, :b)'
it_behaves_like 'immutable objects', <<~RUBY
Struct.new(:node) do
def assignment?
true
end
end
RUBY
end
end
end
end

0 comments on commit 40e63ec

Please sign in to comment.