123456789_123456789_123456789_123456789_123456789_

Class: RuboCop::Cop::Performance::Detect

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, AutoCorrector, Base
Instance Chain:
self, Base
Inherits: Base
  • Object
Defined in: lib/rubocop/cop/performance/detect.rb

Overview

Identifies usages of first, last, [0] or [-1] chained to select, find_all or filter and change them to use detect instead.

Examples:

# bad
[].select { |item| true }.first
[].select { |item| true }.last
[].find_all { |item| true }.first
[].find_all { |item| true }.last
[].filter { |item| true }.first
[].filter { |item| true }.last
[].filter { |item| true }[0]
[].filter { |item| true }[-1]

# good
[].detect { |item| true }
[].reverse.detect { |item| true }

Cop Safety Information:

  • This cop is unsafe because it assumes that the receiver is an Array or equivalent, but can’t reliably detect it. For example, if the receiver is a Hash, it may report a false positive.

Constant Summary

Instance Method Summary

Instance Method Details

#accept_first_call?(receiver, body) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/detect.rb', line 70

def accept_first_call?(receiver, body)
  caller, _first_method, args = *receiver

  # check that we have usual block or block pass
  return true if body.nil? && (args.nil? || !args.block_pass_type?)

  lazy?(caller)
end

#autocorrect(corrector, node, replacement) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/detect.rb', line 102

def autocorrect(corrector, node, replacement)
  receiver, _first_method = *node

  first_range = receiver.source_range.end.join(node.loc.selector)

  receiver, _args, _body = *receiver if receiver.block_type?

  corrector.remove(first_range)
  corrector.replace(receiver.loc.selector, replacement)
end

#lazy?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/detect.rb', line 128

def lazy?(node)
  return false unless node

  receiver, method, _args = *node
  method == :lazy && !receiver.nil?
end

#message_for_method(method, index) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/detect.rb', line 113

def message_for_method(method, index)
  case method
  when :[]
    index == -1 ? INDEX_REVERSE_MSG : INDEX_MSG
  when :last
    REVERSE_MSG
  else
    MSG
  end
end

#on_csend(node)

Alias for #on_send.

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/detect.rb', line 66

alias on_csend on_send

#on_send(node) Also known as: #on_csend

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/detect.rb', line 50

def on_send(node)
  detect_candidate?(node) do |receiver, second_method, args|
    if second_method == :[]
      index = args
      args = {}
    end

    return unless args.empty?
    return unless receiver

    receiver, _args, body = *receiver if receiver.block_type?
    return if accept_first_call?(receiver, body)

    register_offense(node, receiver, second_method, index)
  end
end

#preferred_method (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/detect.rb', line 124

def preferred_method
  config.for_cop('Style/CollectionMethods')['PreferredMethods']['detect'] || 'detect'
end

#register_offense(node, receiver, second_method, index) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/detect.rb', line 79

def register_offense(node, receiver, second_method, index)
  _caller, first_method, _args = *receiver
  range = receiver.loc.selector.join(node.loc.selector)

  message = message_for_method(second_method, index)
  formatted_message = format(message, prefer: preferred_method,
                                      first_method: first_method,
                                      second_method: second_method,
                                      index: index)

  add_offense(range, message: formatted_message) do |corrector|
    autocorrect(corrector, node, replacement(second_method, index))
  end
end

#replacement(method, index) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/detect.rb', line 94

def replacement(method, index)
  if method == :last || (method == :[] && index == -1)
    "reverse.#{preferred_method}"
  else
    preferred_method
  end
end