lib/rubocop/cop/lint/format_parameter_mismatch.rb in rubocop-0.33.0 vs lib/rubocop/cop/lint/format_parameter_mismatch.rb in rubocop-0.34.0
- old
+ new
@@ -10,42 +10,77 @@
# @example
#
# format('A value: %s and another: %i', a_value)
#
class FormatParameterMismatch < Cop
- # http://rubular.com/r/HdWs2uXZv4
+ # http://rubular.com/r/CvpbxkcTzy
MSG = 'Number arguments (%i) to `%s` mismatches expected fields (%i).'
- FIELDS_REGEX = /%([\s#+-0\*])?([0-9]*)?(.[0-9]+)?[bBdiouxXeEfgGacps]/
+ # rubocop:disable Metrics/LineLength
+ FIELD_REGEX = /(%(([\s#+-0\*])?(\d*)?(.\d+)?(\.)?[bBdiouxXeEfgGaAcps]|%))/
+ NAMED_FIELD_REGEX = /%\{[_a-zA-Z][_a-zA-Z]+\}/
def fields_regex
- FIELDS_REGEX
+ FIELD_REGEX
end
def on_send(node)
add_offense(node, :selector) if offending_node?(node)
end
private
def offending_node?(node)
if sprintf?(node) || format?(node) || percent?(node)
- num_of_args_for_format, num_of_expected_fields = count_matches(node)
- num_of_expected_fields != num_of_args_for_format
+ if named_mode?(node)
+ false
+ else
+ num_of_args_for_format, num_of_expected_fields = count_matches(node)
+ num_of_expected_fields != num_of_args_for_format
+ end
else
false
end
end
+ def named_mode?(node)
+ receiver_node, _method_name, *args = *node
+
+ relevant_node = if sprintf?(node) || format?(node)
+ args.first
+ elsif percent?(node)
+ receiver_node
+ end
+
+ relevant_node
+ .loc
+ .expression
+ .source
+ .scan(NAMED_FIELD_REGEX).count > 0
+ end
+
+ def heredoc?(node)
+ _receiver, _name, args = *node
+
+ args.loc.expression.source[0, 2] == '<<'
+ end
+
def count_matches(node)
receiver_node, _method_name, *args = *node
- if sprintf?(node) || format?(node)
+ if (sprintf?(node) || format?(node)) && !heredoc?(node)
number_of_args_for_format = (args.size - 1)
- number_of_expected_fields = expected_fields(args.first).size
+ number_of_expected_fields = expected_fields_count(args.first)
elsif percent?(node)
- number_of_args_for_format = args.first.child_nodes.size
- number_of_expected_fields = expected_fields(receiver_node).size
+ first_child_argument = args.first
+
+ if first_child_argument.type == :array
+ number_of_args_for_format = args.first.child_nodes.size
+ number_of_expected_fields = expected_fields_count(receiver_node)
+ else
+ number_of_args_for_format = 1
+ number_of_expected_fields = expected_fields_count(receiver_node)
+ end
end
[number_of_args_for_format, number_of_expected_fields]
end
@@ -56,16 +91,18 @@
return false unless !receiver && method_name == name
args.size > 1 && :str == args.first.type
end
- def expected_fields(node)
+ def expected_fields_count(node)
node
.loc
.expression
.source
- .scan(FIELDS_REGEX)
+ .scan(FIELD_REGEX)
+ .select { |x| x.first != '%%' }
+ .reduce(0) { |a, e| a + (e[2] == '*' ? 2 : 1) }
end
def format?(node)
format_method?(:format, node)
end
@@ -75,12 +112,18 @@
end
def percent?(node)
receiver_node, method_name, *arg_nodes = *node
- method_name == :% &&
- ([:str, :dstr].include?(receiver_node.type) ||
- arg_nodes[0].type == :array)
+ percent = method_name == :% &&
+ ([:str, :dstr].include?(receiver_node.type) ||
+ arg_nodes[0].type == :array)
+
+ if percent && [:str, :dstr].include?(receiver_node.type)
+ return false if heredoc?(node)
+ end
+
+ percent
end
def message(node)
_receiver, method_name, *_args = *node
num_args_for_format, num_expected_fields = count_matches(node)