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)
@example
# good format('A value: %s and another: %i', a_value, another)
@example
# bad format('Unnumbered format: %s and numbered: %2$s', a_value, another)
@example
# 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 49 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 133 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 115 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 137 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 125 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 129 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 149 def expected_fields_count(node) return :unknown unless node.str_type? 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 164 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 142 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
format_string?(node)
click to toggle source
# File lib/rubocop/cop/lint/format_parameter_mismatch.rb, line 64 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 111 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 68 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 87 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 183 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 101 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 77 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 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 172 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 105 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 168 def sprintf?(node) format_method?(:sprintf, node) end