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."
::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_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 217
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 185
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 274
def argument_without_space?(node) node.argument? && 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 196
def autocorrect(corrector, node) if style == :hash_rockets || force_hash_rockets?(node.parent.pairs) autocorrect_hash_rockets(corrector, node) elsif style == :ruby19_no_mixed_keys || style == :no_mixed_keys 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 278
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 287
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 253
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 238
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 295
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 163
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 177
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 141
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 266
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 159
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 167
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 206
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 210
def word_symbol_pair?(pair) return false unless pair.key.sym_type? || pair.key.dsym_type? acceptable_19_syntax_symbol?(pair.key.source) end