class RuboCop::Cop::Lint::FormatParameterMismatch
This lint sees if there is a mismatch between the number of expected fields for format/sprintf/#% and what is actually passed as arguments.
@example
# bad format('A value: %s and another: %i', a_value)
@example
# good format('A value: %s and another: %i', a_value, another)
Constants
- DIGIT_DOLLAR_FLAG
- FIELD_REGEX
- KERNEL
- MSG
- NAMED_FIELD_REGEX
- NAMED_INTERPOLATION
- PERCENT
- PERCENT_PERCENT
- SHOVEL
- STRING_TYPES
Public Instance Methods
on_send(node)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 36 def on_send(node) return unless offending_node?(node) add_offense(node, location: :selector) end
Private Instance Methods
arguments_count(format)
click to toggle source
number of arguments required for the format sequence
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 149 def arguments_count(format) format.scan('*').count + 1 end
count_format_matches(node)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 111 def count_format_matches(node) [node.arguments.count - 1, expected_fields_count(node.first_argument)] end
count_matches(node)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 93 def count_matches(node) if countable_format?(node) count_format_matches(node) elsif countable_percent?(node) count_percent_matches(node) else [:unknown] * 2 end end
count_percent_matches(node)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 115 def count_percent_matches(node) [node.first_argument.child_nodes.count, expected_fields_count(node.receiver)] end
countable_format?(node)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 103 def countable_format?(node) (sprintf?(node) || format?(node)) && !heredoc?(node) end
countable_percent?(node)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 107 def countable_percent?(node) percent?(node) && node.first_argument.array_type? end
expected_fields_count(node)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 128 def expected_fields_count(node) return :unknown unless node.str_type? return 1 if node.source =~ NAMED_INTERPOLATION max_digit_dollar_num = max_digit_dollar_num(node) return max_digit_dollar_num if max_digit_dollar_num&.nonzero? node .source .scan(FIELD_REGEX) .reject { |x| x.first == PERCENT_PERCENT } .reduce(0) { |acc, elem| acc + arguments_count(elem[2]) } end
format?(node)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 153 def format?(node) format_method?(:format, node) end
format_method?(name, node)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 120 def format_method?(name, node) return false if node.const_receiver? && !node.receiver.loc.name.is?(KERNEL) return false unless node.method?(name) node.arguments.size > 1 && node.first_argument.str_type? end
heredoc?(node)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 89 def heredoc?(node) node.first_argument.source[0, 2] == SHOVEL end
matched_arguments_count?(expected, passed)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 56 def matched_arguments_count?(expected, passed) if passed.negative? expected < passed.abs else expected != passed end end
max_digit_dollar_num(node)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 142 def max_digit_dollar_num(node) node.source.scan(DIGIT_DOLLAR_FLAG).map do |digit_dollar_num| digit_dollar_num.first.to_i end.max end
message(node)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 174 def message(node) num_args_for_format, num_expected_fields = count_matches(node) method_name = node.method?(:%) ? 'String#%' : node.method_name format(MSG, arg_num: num_args_for_format, method: method_name, field_num: num_expected_fields) end
method_with_format_args?(node)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 69 def method_with_format_args?(node) sprintf?(node) || format?(node) || percent?(node) end
named_mode?(node)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 73 def named_mode?(node) relevant_node = if sprintf?(node) || format?(node) node.first_argument elsif percent?(node) node.receiver end !relevant_node.source.scan(NAMED_FIELD_REGEX).empty? end
offending_node?(node)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 44 def offending_node?(node) return false unless called_on_string?(node) return false unless method_with_format_args?(node) return false if named_mode?(node) || splat_args?(node) num_of_format_args, num_of_expected_fields = count_matches(node) return false if num_of_format_args == :unknown matched_arguments_count?(num_of_expected_fields, num_of_format_args) end
percent?(node)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 161 def percent?(node) receiver = node.receiver percent = node.method?(:%) && (STRING_TYPES.include?(receiver.type) || node.first_argument.array_type?) return false if percent && STRING_TYPES.include?(receiver.type) && heredoc?(node) percent end
splat_args?(node)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 83 def splat_args?(node) return false if percent?(node) node.arguments.drop(1).any?(&:splat_type?) end
sprintf?(node)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 157 def sprintf?(node) format_method?(:sprintf, node) end