Class: RuboCop::Cop::Style::MutableConstant
Relationships & Source Files | |
Namespace Children | |
Modules:
| |
Super Chains via Extension / Inclusion / Inheritance | |
Class Chain:
self,
::RuboCop::Cop::AutoCorrector ,
::RuboCop::Cop::Base ,
::RuboCop::ExcludeLimit ,
NodePattern::Macros,
RuboCop::AST::Sexp
|
|
Instance Chain:
|
|
Inherits: |
RuboCop::Cop::Base
|
Defined in: | lib/rubocop/cop/style/mutable_constant.rb |
Overview
Checks whether some constant value isn’t a mutable literal (e.g. array or hash).
Strict mode can be used to freeze all constants, rather than just literals. Strict mode is considered an experimental feature. It has not been updated with an exhaustive list of all methods that will produce frozen objects so there is a decent chance of getting some false positives. Luckily, there is no harm in freezing an already frozen object.
From Ruby 3.0, this cop honours the magic comment 'shareable_constant_value'. When this magic comment is set to any acceptable value other than none, it will suppress the offenses raised by this cop. It enforces frozen state.
Note
|
Regexp and Range literals are frozen objects since Ruby 3.0. |
Note
|
From Ruby 3.0, interpolated strings are not frozen when
# frozen-string-literal: true is used, so this cop enforces explicit
freezing for such strings.
|
Note
|
From Ruby 3.0, this cop allows explicit freezing of constants when
the shareable_constant_value directive is used.
|
Constant Summary
-
MSG =
# File 'lib/rubocop/cop/style/mutable_constant.rb', line 125'Freeze mutable objects assigned to constants.'
::RuboCop::Cop::Base
- Inherited
EMPTY_OFFENSES, RESTRICT_ON_SEND
::RuboCop::Cop::FrozenStringLiteral
- Included
FROZEN_STRING_LITERAL, FROZEN_STRING_LITERAL_ENABLED, FROZEN_STRING_LITERAL_TYPES_RUBY27
::RuboCop::Cop::ConfigurableEnforcedStyle
- Included
Class Attribute Summary
::RuboCop::Cop::AutoCorrector
- Extended
::RuboCop::Cop::Base
- Inherited
.gem_requirements, .lint?, | |
.support_autocorrect? | Returns if class supports autocorrect. |
.support_multiple_source? | Override if your cop should be called repeatedly for multiple investigations Between calls to |
Class Method Summary
::RuboCop::Cop::Base
- Inherited
.autocorrect_incompatible_with | List of cops that should not try to autocorrect at the same time as this cop. |
.badge | Naming. |
.callbacks_needed, .cop_name, .department, | |
.documentation_url | Returns a url to view this cops documentation online. |
.exclude_from_registry | Call for abstract Cop classes. |
.inherited, | |
.joining_forces | Override and return the Force class(es) you need to join. |
.match? | Returns true if the cop name or the cop namespace matches any of the given names. |
.new, | |
.requires_gem | Register a version requirement for the given gem name. |
.restrict_on_send |
::RuboCop::ExcludeLimit
- Extended
exclude_limit | Sets up a configuration option to have an exclude limit tracked. |
transform |
Instance Attribute Summary
::RuboCop::Cop::ConfigurableEnforcedStyle
- Included
::RuboCop::Cop::Base
- Inherited
::RuboCop::Cop::AutocorrectLogic
- Included
Instance Method Summary
- #on_casgn(node)
- #autocorrect(corrector, node) private
- #check(value) private
- #correct_splat_expansion(corrector, expr, splat_value) private
- #frozen_regexp_or_range_literals?(node) ⇒ Boolean private
- #immutable_literal?(node) ⇒ Boolean private
- #mutable_literal?(value) ⇒ Boolean private
- #on_assignment(value) private
-
#operation_produces_immutable_object?(node)
private
Some of these patterns may not actually return an immutable object, but we want to consider them immutable for this cop.
- #range_enclosed_in_parentheses?(node) private
- #requires_parentheses?(node) ⇒ Boolean private
- #shareable_constant_value?(node) ⇒ Boolean private
- #splat_value(node) private
- #strict_check(value) private
::RuboCop::Cop::ConfigurableEnforcedStyle
- Included
::RuboCop::Cop::FrozenStringLiteral
- Included
ShareableConstantValue
- Included
#magic_comment_in_scope | Identifies the most recent magic comment with valid shareable constant values that’s in scope for this node. |
#processed_source_till_node, #recent_shareable_value?, #shareable_constant_value_enabled? |
::RuboCop::Cop::Base
- Inherited
#add_global_offense | Adds an offense that has no particular location. |
#add_offense | Adds an offense on the specified range (or node with an expression) Unless that offense is disabled for this range, a corrector will be yielded to provide the cop the opportunity to autocorrect the offense. |
#begin_investigation | Called before any investigation. |
#callbacks_needed, | |
#cop_config | Configuration Helpers. |
#cop_name, #excluded_file?, | |
#external_dependency_checksum | This method should be overridden when a cop’s behavior depends on state that lives outside of these locations: |
#inspect | |
#message | Gets called if no message is specified when calling |
#name | Alias for Base#cop_name. |
#offenses, | |
#on_investigation_end | Called after all on_… |
#on_new_investigation | Called before all on_… |
#on_other_file | Called instead of all on_… |
#parse | There should be very limited reasons for a Cop to do it’s own parsing. |
#parser_engine, | |
#ready | Called between investigations. |
#relevant_file?, | |
#target_gem_version | Returns a gems locked versions (i.e. |
#target_rails_version, #target_ruby_version, #annotate, #apply_correction, #attempt_correction, | |
#callback_argument | Reserved for Cop::Cop. |
#complete_investigation | Called to complete an investigation. |
#correct, #current_corrector, | |
#current_offense_locations | Reserved for Commissioner: |
#current_offenses, #currently_disabled_lines, #custom_severity, #default_severity, #disable_uncorrectable, #enabled_line?, #file_name_matches_any?, #find_message, #find_severity, #range_for_original, #range_from_node_or_range, | |
#reset_investigation | Actually private methods. |
#use_corrector |
::RuboCop::Cop::AutocorrectLogic
- Included
#disable_offense, #disable_offense_at_end_of_line, #disable_offense_before_and_after, #disable_offense_with_eol_or_surround_comment, #heredoc_range, #max_line_length, #multiline_ranges, #multiline_string?, | |
#range_by_lines | Expand the given range to include all of any lines it covers. |
#range_of_first_line, #range_overlaps_offense?, #string_continuation?, #surrounding_heredoc?, #surrounding_percent_array? |
::RuboCop::Cop::IgnoredNode
- Included
Constructor Details
This class inherits a constructor from RuboCop::Cop::Base
Instance Method Details
#autocorrect(corrector, node) (private)
[ GitHub ]# File 'lib/rubocop/cop/style/mutable_constant.rb', line 168
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
#check(value) (private)
[ GitHub ]# File 'lib/rubocop/cop/style/mutable_constant.rb', line 157
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
#correct_splat_expansion(corrector, expr, splat_value) (private)
[ GitHub ]# File 'lib/rubocop/cop/style/mutable_constant.rb', line 207
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
#frozen_regexp_or_range_literals?(node) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/style/mutable_constant.rb', line 199
def frozen_regexp_or_range_literals?(node) target_ruby_version >= 3.0 && (node.regexp_type? || node.range_type?) end
#immutable_literal?(node) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/style/mutable_constant.rb', line 189
def immutable_literal?(node) frozen_regexp_or_range_literals?(node) || node.immutable_literal? end
#mutable_literal?(value) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/style/mutable_constant.rb', line 183
def mutable_literal?(value) return false if frozen_regexp_or_range_literals?(value) value.mutable_literal? end
#on_assignment(value) (private)
[ GitHub ]# File 'lib/rubocop/cop/style/mutable_constant.rb', line 140
def on_assignment(value) if style == :strict strict_check(value) else check(value) end end
#on_casgn(node)
[ GitHub ]# File 'lib/rubocop/cop/style/mutable_constant.rb', line 127
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
#operation_produces_immutable_object?(node) (private)
Some of these patterns may not actually return an immutable object, but we want to consider them immutable for this cop.
# File 'lib/rubocop/cop/style/mutable_constant.rb', line 223
def_node_matcher :operation_produces_immutable_object?, <<~PATTERN { (const _ _) (send (const {nil? cbase} :Struct) :new ...) (block (send (const {nil? cbase} :Struct) :new ...) ...) (send _ :freeze) (send {float int} {:+ :- :* :** :/ :% :<<} _) (send _ {:+ :- :* :** :/ :%} {float int}) (send _ {:== :=== :!= :<= :>= :< :>} _) (send (const {nil? cbase} :ENV) :[] _) (or (send (const {nil? cbase} :ENV) :[] _) _) (send _ {:count :length :size} ...) (block (send _ {:count :length :size} ...) ...) } PATTERN
#range_enclosed_in_parentheses?(node) (private)
[ GitHub ]# File 'lib/rubocop/cop/style/mutable_constant.rb', line 240
def_node_matcher :range_enclosed_in_parentheses?, <<~PATTERN (begin ({irange erange} _ _)) PATTERN
#requires_parentheses?(node) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/style/mutable_constant.rb', line 203
def requires_parentheses?(node) node.range_type? || (node.send_type? && node.loc.dot.nil?) end
#splat_value(node) (private)
[ GitHub ]# File 'lib/rubocop/cop/style/mutable_constant.rb', line 216
def_node_matcher :splat_value, <<~PATTERN (array (splat $_)) PATTERN
#strict_check(value) (private)
[ GitHub ]# File 'lib/rubocop/cop/style/mutable_constant.rb', line 148
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