Module: RuboCop::Cop::HashShorthandSyntax
Relationships & Source Files | |
Namespace Children | |
Classes:
| |
Extension / Inclusion / Inheritance Descendants | |
Included In:
| |
Defined in: | lib/rubocop/cop/mixin/hash_shorthand_syntax.rb |
Overview
This module checks for Ruby 3.1’s hash value omission syntax.
Constant Summary
-
DO_NOT_MIX_EXPLICIT_VALUE_MSG =
# File 'lib/rubocop/cop/mixin/hash_shorthand_syntax.rb', line 12"#{DO_NOT_MIX_MSG_PREFIX} #{EXPLICIT_HASH_VALUE_MSG}"
-
DO_NOT_MIX_MSG_PREFIX =
# File 'lib/rubocop/cop/mixin/hash_shorthand_syntax.rb', line 10'Do not mix explicit and implicit hash values.'
-
DO_NOT_MIX_OMIT_VALUE_MSG =
# File 'lib/rubocop/cop/mixin/hash_shorthand_syntax.rb', line 11"#{DO_NOT_MIX_MSG_PREFIX} #{OMIT_HASH_VALUE_MSG}"
-
EXPLICIT_HASH_VALUE_MSG =
# File 'lib/rubocop/cop/mixin/hash_shorthand_syntax.rb', line 9'Include the hash value.'
-
OMIT_HASH_VALUE_MSG =
# File 'lib/rubocop/cop/mixin/hash_shorthand_syntax.rb', line 8'Omit the hash value.'
Instance Method Summary
- #on_hash_for_mixed_shorthand(hash_node)
- #on_pair(node)
- #brackets?(method_dispatch_node) ⇒ Boolean private
- #breakdown_value_types_of_hash(hash_node) private
-
#def_node_that_require_parentheses(node)
private
Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity.
- #each_omittable_value_pair(hash_value_type_breakdown, &block) private
- #each_omitted_value_pair(hash_value_type_breakdown, &block) private
- #enforced_shorthand_syntax private
-
#find_ancestor_method_dispatch_node(node)
private
Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity.
- #hash_with_mixed_shorthand_syntax?(hash_value_type_breakdown) ⇒ Boolean private
- #hash_with_values_that_cant_be_omitted?(hash_value_type_breakdown) ⇒ Boolean private
- #ignore_explicit_omissible_hash_shorthand_syntax?(hash_value_type_breakdown) ⇒ Boolean private
- #ignore_hash_shorthand_syntax?(pair_node) ⇒ Boolean private
- #ignore_mixed_hash_shorthand_syntax?(hash_node) ⇒ Boolean private
- #last_expression?(node) ⇒ Boolean private
- #method_dispatch_as_argument?(method_dispatch_node) ⇒ Boolean private
- #mixed_shorthand_syntax_check(hash_value_type_breakdown) private
- #no_mixed_shorthand_syntax_check(hash_value_type_breakdown) private
- #register_offense(node, message, replacement) private
- #require_hash_value?(hash_key_source, node) ⇒ Boolean private
- #require_hash_value_for_around_hash_literal?(node) ⇒ Boolean private
- #use_element_of_hash_literal_as_receiver?(ancestor, parent) ⇒ Boolean private
- #use_modifier_form_without_parenthesized_method_call?(ancestor) ⇒ Boolean private
Instance Method Details
#brackets?(method_dispatch_node) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/mixin/hash_shorthand_syntax.rb', line 126
def brackets?(method_dispatch_node) method_dispatch_node.method?(:[]) || method_dispatch_node.method?(:[]=) end
#breakdown_value_types_of_hash(hash_node) (private)
[ GitHub ]# File 'lib/rubocop/cop/mixin/hash_shorthand_syntax.rb', line 156
def breakdown_value_types_of_hash(hash_node) hash_node.pairs.group_by do |pair_node| if pair_node.value_omission? :value_omitted elsif require_hash_value?(pair_node.key.source, pair_node) :value_needed else :value_omittable end end end
#def_node_that_require_parentheses(node) (private)
Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
# File 'lib/rubocop/cop/mixin/hash_shorthand_syntax.rb', line 103
def def_node_that_require_parentheses(node) last_pair = node.parent.pairs.last return unless last_pair.key.source == last_pair.value.source return unless (dispatch_node = find_ancestor_method_dispatch_node(node)) return if dispatch_node.assignment_method? return if dispatch_node.parenthesized? return if dispatch_node.parent && parentheses?(dispatch_node.parent) return if last_expression?(dispatch_node) && !method_dispatch_as_argument?(dispatch_node) def_node = node.each_ancestor(:send, :csend, :super, :yield).first DefNode.new(def_node) unless def_node && def_node.arguments.empty? end
#each_omittable_value_pair(hash_value_type_breakdown, &block) (private)
[ GitHub ]# File 'lib/rubocop/cop/mixin/hash_shorthand_syntax.rb', line 185
def each_omittable_value_pair(hash_value_type_breakdown, &block) hash_value_type_breakdown[:value_omittable]&.each(&block) end
#each_omitted_value_pair(hash_value_type_breakdown, &block) (private)
[ GitHub ]# File 'lib/rubocop/cop/mixin/hash_shorthand_syntax.rb', line 181
def each_omitted_value_pair(hash_value_type_breakdown, &block) hash_value_type_breakdown[:value_omitted]&.each(&block) end
#enforced_shorthand_syntax (private)
[ GitHub ]# File 'lib/rubocop/cop/mixin/hash_shorthand_syntax.rb', line 81
def enforced_shorthand_syntax cop_config.fetch('EnforcedShorthandSyntax', 'always') end
#find_ancestor_method_dispatch_node(node) (private)
Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
# File 'lib/rubocop/cop/mixin/hash_shorthand_syntax.rb', line 118
def find_ancestor_method_dispatch_node(node) return unless (ancestor = node.parent.parent) return unless ancestor.call_type? || ancestor.super_type? || ancestor.yield_type? return if brackets?(ancestor) ancestor end
#hash_with_mixed_shorthand_syntax?(hash_value_type_breakdown) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/mixin/hash_shorthand_syntax.rb', line 168
def hash_with_mixed_shorthand_syntax?(hash_value_type_breakdown) hash_value_type_breakdown.keys.size > 1 end
#hash_with_values_that_cant_be_omitted?(hash_value_type_breakdown) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/mixin/hash_shorthand_syntax.rb', line 172
def hash_with_values_that_cant_be_omitted?(hash_value_type_breakdown) hash_value_type_breakdown[:value_needed]&.any? end
#ignore_explicit_omissible_hash_shorthand_syntax?(hash_value_type_breakdown) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/mixin/hash_shorthand_syntax.rb', line 176
def ignore_explicit_omissible_hash_shorthand_syntax?(hash_value_type_breakdown) hash_value_type_breakdown.keys == [:value_omittable] && enforced_shorthand_syntax == 'either_consistent' end
#ignore_hash_shorthand_syntax?(pair_node) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/mixin/hash_shorthand_syntax.rb', line 75
def ignore_hash_shorthand_syntax?(pair_node) target_ruby_version <= 3.0 || enforced_shorthand_syntax == 'either' || %w[consistent either_consistent].include?(enforced_shorthand_syntax) || !pair_node.parent.hash_type? end
#ignore_mixed_hash_shorthand_syntax?(hash_node) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/mixin/hash_shorthand_syntax.rb', line 69
def ignore_mixed_hash_shorthand_syntax?(hash_node) target_ruby_version <= 3.0 || !%w[consistent either_consistent].include?(enforced_shorthand_syntax) || !hash_node.hash_type? end
#last_expression?(node) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/mixin/hash_shorthand_syntax.rb', line 141
def last_expression?(node) return false if node.right_sibling return true unless (assignment_node = node.each_ancestor.find(&:assignment?)) return last_expression?(assignment_node.parent) if assignment_node.parent&.assignment? !assignment_node.right_sibling end
#method_dispatch_as_argument?(method_dispatch_node) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/mixin/hash_shorthand_syntax.rb', line 149
def method_dispatch_as_argument?(method_dispatch_node) parent = method_dispatch_node.parent return false unless parent parent.call_type? || parent.super_type? || parent.yield_type? end
#mixed_shorthand_syntax_check(hash_value_type_breakdown) (private)
[ GitHub ]# File 'lib/rubocop/cop/mixin/hash_shorthand_syntax.rb', line 189
def mixed_shorthand_syntax_check(hash_value_type_breakdown) if hash_with_values_that_cant_be_omitted?(hash_value_type_breakdown) each_omitted_value_pair(hash_value_type_breakdown) do |pair_node| hash_key_source = pair_node.key.source replacement = "#{hash_key_source}: #{hash_key_source}" register_offense(pair_node, DO_NOT_MIX_EXPLICIT_VALUE_MSG, replacement) end else each_omittable_value_pair(hash_value_type_breakdown) do |pair_node| hash_key_source = pair_node.key.source replacement = "#{hash_key_source}:" register_offense(pair_node, DO_NOT_MIX_OMIT_VALUE_MSG, replacement) end end end
#no_mixed_shorthand_syntax_check(hash_value_type_breakdown) (private)
[ GitHub ]# File 'lib/rubocop/cop/mixin/hash_shorthand_syntax.rb', line 205
def no_mixed_shorthand_syntax_check(hash_value_type_breakdown) return if hash_with_values_that_cant_be_omitted?(hash_value_type_breakdown) return if ignore_explicit_omissible_hash_shorthand_syntax?(hash_value_type_breakdown) each_omittable_value_pair(hash_value_type_breakdown) do |pair_node| hash_key_source = pair_node.key.source replacement = "#{hash_key_source}:" register_offense(pair_node, OMIT_HASH_VALUE_MSG, replacement) end end
#on_hash_for_mixed_shorthand(hash_node)
[ GitHub ]# File 'lib/rubocop/cop/mixin/hash_shorthand_syntax.rb', line 14
def on_hash_for_mixed_shorthand(hash_node) return if ignore_mixed_hash_shorthand_syntax?(hash_node) hash_value_type_breakdown = breakdown_value_types_of_hash(hash_node) if hash_with_mixed_shorthand_syntax?(hash_value_type_breakdown) mixed_shorthand_syntax_check(hash_value_type_breakdown) else no_mixed_shorthand_syntax_check(hash_value_type_breakdown) end end
#on_pair(node)
[ GitHub ]# File 'lib/rubocop/cop/mixin/hash_shorthand_syntax.rb', line 26
def on_pair(node) return if ignore_hash_shorthand_syntax?(node) hash_key_source = node.key.source if enforced_shorthand_syntax == 'always' return if node.value_omission? || require_hash_value?(hash_key_source, node) = OMIT_HASH_VALUE_MSG replacement = "#{hash_key_source}:" self.config_to_allow_offenses = { 'Enabled' => false } else return unless node.value_omission? = EXPLICIT_HASH_VALUE_MSG replacement = "#{hash_key_source}: #{hash_key_source}" end register_offense(node, , replacement) end
#register_offense(node, message, replacement) (private)
[ GitHub ]# File 'lib/rubocop/cop/mixin/hash_shorthand_syntax.rb', line 49
def register_offense(node, , replacement) # rubocop:disable Metrics/AbcSize add_offense(node.value, message: ) do |corrector| corrector.replace(node, replacement) next unless (def_node = def_node_that_require_parentheses(node)) last_argument = def_node.last_argument if last_argument.nil? || !last_argument.hash_type? next corrector.replace(node, replacement) end white_spaces = range_between(def_node.selector.end_pos, def_node.first_argument.source_range.begin_pos) next if node.parent.braces? corrector.replace(white_spaces, '(') corrector.insert_after(last_argument, ')') if node == last_argument.pairs.last end end
#require_hash_value?(hash_key_source, node) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/mixin/hash_shorthand_syntax.rb', line 85
def require_hash_value?(hash_key_source, node) return true if !node.key.sym_type? || require_hash_value_for_around_hash_literal?(node) hash_value = node.value return true unless hash_value.send_type? || hash_value.lvar_type? hash_key_source != hash_value.source || hash_key_source.end_with?('!', '?') end
#require_hash_value_for_around_hash_literal?(node) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/mixin/hash_shorthand_syntax.rb', line 94
def require_hash_value_for_around_hash_literal?(node) return false unless (method_dispatch_node = find_ancestor_method_dispatch_node(node)) !node.parent.braces? && !use_element_of_hash_literal_as_receiver?(method_dispatch_node, node.parent) && use_modifier_form_without_parenthesized_method_call?(method_dispatch_node) end
#use_element_of_hash_literal_as_receiver?(ancestor, parent) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/mixin/hash_shorthand_syntax.rb', line 130
def use_element_of_hash_literal_as_receiver?(ancestor, parent) # `{value:}.do_something` is a valid syntax. ancestor.send_type? && ancestor.receiver == parent end
#use_modifier_form_without_parenthesized_method_call?(ancestor) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/mixin/hash_shorthand_syntax.rb', line 135
def use_modifier_form_without_parenthesized_method_call?(ancestor) return false if ancestor.respond_to?(:parenthesized?) && ancestor.parenthesized? ancestor.ancestors.any? { |node| node.respond_to?(:modifier_form?) && node.modifier_form? } end