Class: RuboCop::Cop::Style::HashSyntax
Relationships & Source Files | |
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/hash_syntax.rb |
Overview
Checks hash literal syntax.
It can enforce either the use of the class hash rocket syntax or the use of the newer Ruby 1.9 syntax (when applicable).
A separate offense is registered for each problematic pair.
The supported styles are:
-
ruby19 - forces use of the 1.9 syntax (e.g.
{a: 1}
) when hashes have all symbols for keys -
hash_rockets - forces use of hash rockets for all hashes
-
no_mixed_keys - simply checks for hashes with mixed syntaxes
-
ruby19_no_mixed_keys - forces use of ruby 1.9 syntax and forbids mixed syntax hashes
This cop has EnforcedShorthandSyntax
option.
It can enforce either the use of the explicit hash value syntax or
the use of Ruby 3.1’s hash value shorthand syntax.
The supported styles are:
-
always - forces use of the 3.1 syntax (e.g.
foo:
) -
never - forces use of explicit hash literal value
-
either - accepts both shorthand and explicit use of hash literal value
-
consistent - forces use of the 3.1 syntax only if all values can be omitted in the hash
-
either_consistent - accepts both shorthand and explicit use of hash literal value, but they must be consistent
Constant Summary
-
MSG_19 =
# File 'lib/rubocop/cop/style/hash_syntax.rb', line 137'Use the new Ruby 1.9 hash syntax.'
-
MSG_HASH_ROCKETS =
# File 'lib/rubocop/cop/style/hash_syntax.rb', line 139'Use hash rockets syntax.'
-
MSG_NO_MIXED_KEYS =
# File 'lib/rubocop/cop/style/hash_syntax.rb', line 138"Don't mix styles in the same hash."
-
NO_MIXED_KEYS_STYLES =
# File 'lib/rubocop/cop/style/hash_syntax.rb', line 140%i[ruby19_no_mixed_keys no_mixed_keys].freeze
::RuboCop::Cop::Base
- Inherited
EMPTY_OFFENSES, RESTRICT_ON_SEND
::RuboCop::Cop::ConfigurableEnforcedStyle
- Included
::RuboCop::Cop::HashShorthandSyntax
- Included
DO_NOT_MIX_EXPLICIT_VALUE_MSG, DO_NOT_MIX_MSG_PREFIX, DO_NOT_MIX_OMIT_VALUE_MSG, EXPLICIT_HASH_VALUE_MSG, OMIT_HASH_VALUE_MSG
::RuboCop::Cop::RangeHelp
- 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
- #alternative_style
- #hash_rockets_check(pairs)
- #no_mixed_keys_check(pairs)
- #on_hash(node)
- #ruby19_check(pairs)
- #ruby19_no_mixed_keys_check(pairs)
- #acceptable_19_syntax_symbol?(sym_name) ⇒ Boolean private
- #argument_without_space?(node) ⇒ Boolean private
- #autocorrect(corrector, node) private
- #autocorrect_hash_rockets(corrector, pair_node) private
- #autocorrect_no_mixed_keys(corrector, pair_node) private
- #autocorrect_ruby19(corrector, pair_node) private
- #check(pairs, delim, msg) private
- #force_hash_rockets?(pairs) ⇒ Boolean private
- #range_for_autocorrect_ruby19(pair_node) private
- #sym_indices?(pairs) ⇒ Boolean private
- #word_symbol_pair?(pair) ⇒ Boolean private
::RuboCop::Cop::RangeHelp
- Included
#add_range, #column_offset_between, | |
#contents_range | A range containing only the contents of a literal with delimiters (e.g. |
#directions, | |
#effective_column | Returns the column attribute of the range, except if the range is on the first line and there’s a byte order mark at the beginning of that line, in which case 1 is subtracted from the column value. |
#final_pos, #move_pos, #move_pos_str, #range_between, #range_by_whole_lines, #range_with_comments, #range_with_comments_and_lines, #range_with_surrounding_comma, #range_with_surrounding_space, #source_range |
::RuboCop::Cop::HashShorthandSyntax
- Included
::RuboCop::Cop::ConfigurableEnforcedStyle
- Included
::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
#acceptable_19_syntax_symbol?(sym_name) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/style/hash_syntax.rb', line 218
def acceptable_19_syntax_symbol?(sym_name) sym_name.delete_prefix!(':') if cop_config['PreferHashRocketsForNonAlnumEndingSymbols'] && # Prefer { :production? => false } over { production?: false } and # similarly for other non-alnum final characters (except quotes, # to prefer { "x y": 1 } over { :"x y" => 1 }). !/[\p{Alnum}"']\z/.match?(sym_name) return false end # Most hash keys can be matched against a simple regex. return true if /\A[_a-z]\w*[?!]?\z/i.match?(sym_name) return false if target_ruby_version <= 2.1 (sym_name.start_with?("'") && sym_name.end_with?("'")) || (sym_name.start_with?('"') && sym_name.end_with?('"')) end
#alternative_style
[ GitHub ]# File 'lib/rubocop/cop/style/hash_syntax.rb', line 186
def alternative_style case style when :hash_rockets :ruby19 when :ruby19, :ruby19_no_mixed_keys :hash_rockets end end
#argument_without_space?(node) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/style/hash_syntax.rb', line 275
def argument_without_space?(node) return false if !node.argument? || !node.parent.loc.selector node.source_range.begin_pos == node.parent.loc.selector.end_pos end
#autocorrect(corrector, node) (private)
[ GitHub ]# File 'lib/rubocop/cop/style/hash_syntax.rb', line 197
def autocorrect(corrector, node) if style == :hash_rockets || force_hash_rockets?(node.parent.pairs) autocorrect_hash_rockets(corrector, node) elsif NO_MIXED_KEYS_STYLES.include?(style) autocorrect_no_mixed_keys(corrector, node) else autocorrect_ruby19(corrector, node) end end
#autocorrect_hash_rockets(corrector, pair_node) (private)
[ GitHub ]# File 'lib/rubocop/cop/style/hash_syntax.rb', line 281
def autocorrect_hash_rockets(corrector, pair_node) op = pair_node.loc.operator key_with_hash_rocket = ":#{pair_node.key.source}#{pair_node.inverse_delimiter(true)}" key_with_hash_rocket += pair_node.key.source if pair_node.value_omission? corrector.replace(pair_node.key, key_with_hash_rocket) corrector.remove(range_with_surrounding_space(op)) end
#autocorrect_no_mixed_keys(corrector, pair_node) (private)
[ GitHub ]# File 'lib/rubocop/cop/style/hash_syntax.rb', line 290
def autocorrect_no_mixed_keys(corrector, pair_node) if pair_node.colon? autocorrect_hash_rockets(corrector, pair_node) else autocorrect_ruby19(corrector, pair_node) end end
#autocorrect_ruby19(corrector, pair_node) (private)
[ GitHub ]# File 'lib/rubocop/cop/style/hash_syntax.rb', line 254
def autocorrect_ruby19(corrector, pair_node) range = range_for_autocorrect_ruby19(pair_node) space = argument_without_space?(pair_node.parent) ? ' ' : '' corrector.replace(range, range.source.sub(/^:(.*\S)\s*=>\s*$/, "#{space}\\1: ")) hash_node = pair_node.parent return unless hash_node.parent&.return_type? && !hash_node.braces? corrector.wrap(hash_node, '{', '}') end
#check(pairs, delim, msg) (private)
[ GitHub ]# File 'lib/rubocop/cop/style/hash_syntax.rb', line 239
def check(pairs, delim, msg) pairs.each do |pair| if pair.delimiter == delim location = pair.source_range.begin.join(pair.loc.operator) add_offense(location, message: msg) do |corrector| autocorrect(corrector, pair) opposite_style_detected end else correct_style_detected end end end
#force_hash_rockets?(pairs) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/style/hash_syntax.rb', line 298
def force_hash_rockets?(pairs) cop_config['UseHashRocketsWithSymbolValues'] && pairs.map(&:value).any?(&:sym_type?) end
#hash_rockets_check(pairs)
[ GitHub ]# File 'lib/rubocop/cop/style/hash_syntax.rb', line 164
def hash_rockets_check(pairs) check(pairs, ':', MSG_HASH_ROCKETS) end
#no_mixed_keys_check(pairs)
[ GitHub ]# File 'lib/rubocop/cop/style/hash_syntax.rb', line 178
def no_mixed_keys_check(pairs) if sym_indices?(pairs) check(pairs, pairs.first.inverse_delimiter, MSG_NO_MIXED_KEYS) else check(pairs, ':', MSG_NO_MIXED_KEYS) end end
#on_hash(node)
[ GitHub ]# File 'lib/rubocop/cop/style/hash_syntax.rb', line 142
def on_hash(node) pairs = node.pairs return if pairs.empty? on_hash_for_mixed_shorthand(node) if style == :hash_rockets || force_hash_rockets?(pairs) hash_rockets_check(pairs) elsif style == :ruby19_no_mixed_keys ruby19_no_mixed_keys_check(pairs) elsif style == :no_mixed_keys no_mixed_keys_check(pairs) else ruby19_check(pairs) end end
#range_for_autocorrect_ruby19(pair_node) (private)
[ GitHub ]# File 'lib/rubocop/cop/style/hash_syntax.rb', line 267
def range_for_autocorrect_ruby19(pair_node) key = pair_node.key.source_range operator = pair_node.loc.operator range = key.join(operator) range_with_surrounding_space(range, side: :right) end
#ruby19_check(pairs)
[ GitHub ]# File 'lib/rubocop/cop/style/hash_syntax.rb', line 160
def ruby19_check(pairs) check(pairs, '=>', MSG_19) if sym_indices?(pairs) end
#ruby19_no_mixed_keys_check(pairs)
[ GitHub ]# File 'lib/rubocop/cop/style/hash_syntax.rb', line 168
def ruby19_no_mixed_keys_check(pairs) if force_hash_rockets?(pairs) check(pairs, ':', MSG_HASH_ROCKETS) elsif sym_indices?(pairs) check(pairs, '=>', MSG_19) else check(pairs, ':', MSG_NO_MIXED_KEYS) end end
#sym_indices?(pairs) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/style/hash_syntax.rb', line 207
def sym_indices?(pairs) pairs.all? { |p| word_symbol_pair?(p) } end
#word_symbol_pair?(pair) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/style/hash_syntax.rb', line 211
def word_symbol_pair?(pair) return false unless pair.key.sym_type? || pair.key.dsym_type? acceptable_19_syntax_symbol?(pair.key.source) end