lib/rubocop/cop/style/symbol_proc.rb



# encoding: utf-8

module RuboCop
  module Cop
    module Style
      # Use symbols as procs when possible.
      #
      # @example
      #   # bad
      #   something.map { |s| s.upcase }
      #
      #   # good
      #   something.map(&:upcase)
      class SymbolProc < Cop
        MSG = 'Pass `&:%s` as an argument to `%s` instead of a block.'

        PROC_NODE = s(:send, s(:const, nil, :Proc), :new)

        def on_block(node)
          block_send, block_args, block_body = *node

          _breceiver, bmethod_name, bargs = *block_send

          # TODO: Rails-specific handling that we should probably make
          # configurable - https://github.com/bbatsov/rubocop/issues/1485
          # we should ignore lambdas & procs
          return if block_send == PROC_NODE
          return if [:lambda, :proc].include?(bmethod_name)
          return if ignored_method?(bmethod_name)
          # File.open(file) { |f| f.readlines }
          return if bargs
          return unless can_shorten?(block_args, block_body)

          _receiver, method_name, _args = *block_body
          add_offense(node,
                      :expression,
                      format(MSG,
                             method_name,
                             bmethod_name))
        end

        def autocorrect(node)
          lambda do |corrector|
            _block_method, _block_args, block_body = *node
            _receiver, method_name, _args = *block_body

            block_range =
              Parser::Source::Range.new(node.loc.expression.source_buffer,
                                        node.loc.begin.begin_pos,
                                        node.loc.end.end_pos)

            corrector.replace(range_with_surrounding_space(block_range, :left),
                              "(&:#{method_name})")
          end
        end

        def ignored_methods
          cop_config['IgnoredMethods']
        end

        def ignored_method?(name)
          ignored_methods.include?(name.to_s)
        end

        def can_shorten?(block_args, block_body)
          # something { |x, y| ... }
          return false unless block_args.children.size == 1
          return false unless block_body && block_body.type == :send

          receiver, _method_name, args = *block_body

          # method in block must be invoked on a lvar without args
          return false if args
          return false unless receiver && receiver.type == :lvar

          block_arg_name, = *block_args.children.first
          receiver_name, = *receiver

          block_arg_name == receiver_name
        end
      end
    end
  end
end