class RuboCop::Cop::Performance::RedundantStringChars
str.chars.drop(2) # Incompatible with ‘str.chars`.
str.chars.last(2) # Incompatible with `str.chars`.
# If a negative value is specified for the receiver, `nil` is returned.
# For example, if the receiver is an empty string, it will be incompatible.<br><br>str.empty?<br>str.size<br>str.length<br>str.chars
# good
str.chars.empty?
str.chars.size
str.chars.length
str.chars.take(2)
# bad<br><br>str[-1]<br>str.chars
str[0]
# good
str.chars.first(2)
str.chars.first
# bad<br><br>str.chars
# good
str.chars.last
str.chars.slice(0..2)
str.chars[0..2]
# bad
@example
Checks for redundant `String#chars`.
def build_bad_method(method, args)
def build_bad_method(method, args) case method when :[] "chars[#{build_call_args(args)}]" else if args.any? "chars.#{method}(#{build_call_args(args)})" else "chars.#{method}" end end end
def build_call_args(call_args_node)
def build_call_args(call_args_node) call_args_node.map(&:source).join(', ') end
def build_good_method(method, args)
def build_good_method(method, args) case method when :slice "[#{build_call_args(args)}].chars" when :[], :first build_good_method_for_brackets_or_first_method(method, args) when :last '[-1]' when :take "[0...#{args.first.source}].chars" else ".#{method}" end end
def build_good_method_for_brackets_or_first_method(method, args)
def build_good_method_for_brackets_or_first_method(method, args) first_arg = args.first if first_arg&.range_type? "[#{build_call_args(args)}].chars" elsif method == :first && args.any? "[0...#{args.first.source}].chars" else first_arg ? "[#{first_arg.source}]" : '[0]' end end
def build_message(method, args)
def build_message(method, args) good_method = build_good_method(method, args) bad_method = build_bad_method(method, args) format(MSG, good_method: good_method, bad_method: bad_method) end
def correction_range(receiver, node)
def correction_range(receiver, node) range_between(receiver.loc.dot.begin_pos, node.source_range.end_pos) end
def offense_range(receiver, node)
def offense_range(receiver, node) range_between(receiver.loc.selector.begin_pos, node.source_range.end_pos) end
def on_send(node)
def on_send(node) return unless (receiver, method, args = redundant_chars_call?(node)) return if method == :last && !args.empty? range = offense_range(receiver, node) message = build_message(method, args) add_offense(range, message: message) do |corrector| range = correction_range(receiver, node) replacement = build_good_method(method, args) corrector.replace(range, replacement) end end