class RuboCop::Cop::Style::MutableConstant
CONST = [1, 2, 3]
# shareable_constant_value: literal
# good
CONST = [1, 2, 3]
# bad
# Magic comment - shareable_constant_value: literal
@example
end.freeze
end
puts 1
def foo
CONST = Struct.new do
# good
CONST = Something.new.freeze
# good
end
end
puts 1
def foo
CONST = Struct.new do
# bad
CONST = Something.new
# bad
@example EnforcedStyle: strict
CONST = Something.new
# good
TESTING
This is a heredoc
CONST = <<~TESTING.freeze
# good
CONST = [1, 2, 3].freeze
# good
CONST = [1, 2, 3]
# bad
@example EnforcedStyle: literals (default)
and will need to be manually refactored.
are made frozen will change from being accepted to raising ‘FrozenError`,
This cop’s autocorrection is unsafe since any mutations on objects that
@safety
the ‘shareable_constant_value` directive is used.
NOTE: From Ruby 3.0, this cop allows explicit freezing of constants when
freezing for such strings.
`# frozen-string-literal: true` is used, so this cop enforces explicit
NOTE: From Ruby 3.0, interpolated strings are not frozen when
NOTE: `Regexp` and `Range` literals are frozen objects since Ruby 3.0.
raised by this cop. It enforces frozen state.
acceptable value other than none, it will suppress the offenses
’shareable_constant_value’. When this magic comment is set to any
From Ruby 3.0, this cop honours the magic comment
frozen object.
positives. Luckily, there is no harm in freezing an already
frozen objects so there is a decent chance of getting some false
updated with an exhaustive list of all methods that will produce
Strict mode is considered an experimental feature. It has not been
just literals.
Strict mode can be used to freeze all constants, rather than
mutable literal (e.g. array or hash).
Checks whether some constant value isn’t a
def autocorrect(corrector, node)
def autocorrect(corrector, node) expr = node.source_range splat_value = splat_value(node) if splat_value correct_splat_expansion(corrector, expr, splat_value) elsif node.array_type? && !node.bracketed? corrector.wrap(expr, '[', ']') elsif requires_parentheses?(node) corrector.wrap(expr, '(', ')') end corrector.insert_after(expr, '.freeze') end
def check(value)
def check(value) range_enclosed_in_parentheses = range_enclosed_in_parentheses?(value) return unless mutable_literal?(value) || (target_ruby_version <= 2.7 && range_enclosed_in_parentheses) return if frozen_string_literal?(value) return if shareable_constant_value?(value) add_offense(value) { |corrector| autocorrect(corrector, value) } end
def correct_splat_expansion(corrector, expr, splat_value)
def correct_splat_expansion(corrector, expr, splat_value) if range_enclosed_in_parentheses?(splat_value) corrector.replace(expr, "#{splat_value.source}.to_a") else corrector.replace(expr, "(#{splat_value.source}).to_a") end end
def frozen_regexp_or_range_literals?(node)
def frozen_regexp_or_range_literals?(node) target_ruby_version >= 3.0 && node.type?(:regexp, :range) end
def immutable_literal?(node)
def immutable_literal?(node) frozen_regexp_or_range_literals?(node) || node.immutable_literal? end
def mutable_literal?(value)
def mutable_literal?(value) return false if frozen_regexp_or_range_literals?(value) value.mutable_literal? end
def on_assignment(value)
def on_assignment(value) if style == :strict strict_check(value) else check(value) end end
def on_casgn(node)
def on_casgn(node) if node.expression.nil? # This is only the case for `CONST += ...` or similarg66 parent = node.parent return unless parent.or_asgn_type? # We only care about `CONST ||= ...` on_assignment(parent.children.last) else on_assignment(node.expression) end end
def requires_parentheses?(node)
def requires_parentheses?(node) node.range_type? || (node.send_type? && node.loc.dot.nil?) end
def shareable_constant_value?(node)
def shareable_constant_value?(node) return false if target_ruby_version < 3.0 recent_shareable_value? node end
def strict_check(value)
def strict_check(value) return if immutable_literal?(value) return if operation_produces_immutable_object?(value) return if frozen_string_literal?(value) return if shareable_constant_value?(value) add_offense(value) { |corrector| autocorrect(corrector, value) } end