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.
In addition it checks whether different formats are used in the same format string. Do not mix numbered, unnumbered, and named formats in the same format string.
@example
# bad format('A value: %s and another: %i', a_value) # good format('A value: %s and another: %i', a_value, another) # bad format('Unnumbered format: %s and numbered: %2$s', a_value, another) # good format('Numbered format: %1$s and numbered %2$s', a_value, another)
Constants
- KERNEL
- MSG
- MSG_INVALID
- RESTRICT_ON_SEND
- SHOVEL
- STRING_TYPES
Public Instance Methods
on_send(node)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 39 def on_send(node) return unless format_string?(node) if invalid_format_string?(node) add_offense(node.loc.selector, message: MSG_INVALID) return end return unless offending_node?(node) add_offense(node.loc.selector, message: message(node)) end
Private Instance Methods
count_format_matches(node)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 127 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 109 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 131 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 119 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 123 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 143 def expected_fields_count(node) return :unknown unless string_type?(node) format_string = RuboCop::Cop::Utils::FormatString.new(node.source) return 1 if format_string.named_interpolation? max_digit_dollar_num = format_string.max_digit_dollar_num return max_digit_dollar_num if max_digit_dollar_num&.nonzero? format_string .format_sequences .reject(&:percent?) .reduce(0) { |acc, seq| acc + seq.arity } end
format?(node)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 158 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 136 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 && string_type?(node.first_argument) end
format_string?(node)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 54 def format_string?(node) called_on_string?(node) && method_with_format_args?(node) end
heredoc?(node)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 105 def heredoc?(node) node.first_argument.source[0, 2] == SHOVEL end
invalid_format_string?(node)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 58 def invalid_format_string?(node) string = if sprintf?(node) || format?(node) node.first_argument.source else node.receiver.source end !RuboCop::Cop::Utils::FormatString.new(string).valid? end
matched_arguments_count?(expected, passed)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 81 def matched_arguments_count?(expected, passed) if passed.negative? expected < passed.abs else expected != passed end end
message(node)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 176 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 95 def method_with_format_args?(node) sprintf?(node) || format?(node) || percent?(node) end
offending_node?(node)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 67 def offending_node?(node) return false if splat_args?(node) num_of_format_args, num_of_expected_fields = count_matches(node) return false if num_of_format_args == :unknown first_arg = node.first_argument return false if num_of_expected_fields.zero? && (first_arg.dstr_type? || first_arg.array_type?) 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 166 def percent?(node) receiver = node.receiver percent = node.method?(:%) && (string_type?(receiver) || node.first_argument.array_type?) return false if percent && string_type?(receiver) && heredoc?(node) percent end
splat_args?(node)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 99 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 162 def sprintf?(node) format_method?(:sprintf, node) end
string_type?(node)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 185 def string_type?(node) STRING_TYPES.include?(node.type) end