lib/rubocop/cop/style/lambda.rb in rubocop-0.39.0 vs lib/rubocop/cop/style/lambda.rb in rubocop-0.40.0

- old
+ new

@@ -2,74 +2,142 @@ # frozen_string_literal: true module RuboCop module Cop module Style - # This cop checks for uses of the pre 1.9 lambda syntax for one-line - # anonymous functions and uses of the 1.9 lambda syntax for multi-line - # anonymous functions. + # This cop (by default) checks for uses of the lambda literal syntax for + # single line lambdas, and the method call syntax for multiline lambdas. + # It is configurable to enforce one of the styles for both single line + # and multiline lambdas as well. + # + # @example + # + # # EnforcedStyle: line_count_dependent (default) + # + # # bad + # f = lambda { |x| x } + # f = ->(x) do + # x + # end + # + # # good + # f = ->(x) { x } + # f = lambda do |x| + # x + # end + # + # @example + # + # # EnforcedStyle: lambda + # + # # bad + # f = ->(x) { x } + # f = ->(x) do + # x + # end + # + # # good + # f = lambda { |x| x } + # f = lambda do |x| + # x + # end + # + # @example + # + # # EnforcedStyle: literal + # + # # bad + # f = lambda { |x| x } + # f = lambda do |x| + # x + # end + # + # # good + # f = ->(x) { x } + # f = ->(x) do + # x + # end class Lambda < Cop - SINGLE_MSG = 'Use the new lambda literal syntax ' \ - '`->(params) {...}`.'.freeze - SINGLE_NO_ARG_MSG = 'Use the new lambda literal syntax ' \ - '`-> {...}`.'.freeze - MULTI_MSG = 'Use the `lambda` method for multi-line lambdas.'.freeze + include ConfigurableEnforcedStyle + LITERAL_MESSAGE = 'Use the `-> { ... }` lambda literal syntax for ' \ + '%s lambdas.'.freeze + METHOD_MESSAGE = 'Use the `lambda` method for %s lambdas.'.freeze + + OFFENDING_SELECTORS = { + style: { + lambda: { single_line: '->', multiline: '->' }, + literal: { single_line: 'lambda', multiline: 'lambda' }, + line_count_dependent: { single_line: 'lambda', multiline: '->' } + } + }.freeze + TARGET = s(:send, nil, :lambda) def on_block(node) # We're looking for # (block # (send nil :lambda) # ...) - block_method, args, = *node + block_method, _args, = *node return unless block_method == TARGET - selector = block_method.source - length = lambda_length(node) - if selector != '->' && length == 1 - add_offense_for_single_line(node, block_method.source_range, args) - elsif selector == '->' && length > 1 - add_offense(node, block_method.source_range, MULTI_MSG) - end + check(node) end private - def add_offense_for_single_line(block_node, location, args) - if args.children.empty? - add_offense(block_node, location, SINGLE_NO_ARG_MSG) - else - add_offense(block_node, location, SINGLE_MSG) + def check(node) + block_method, _args, = *node + + selector = block_method.source + + if offending_selector?(node, selector) + add_offense(node, block_method.source_range, + message(node, selector)) end end - def lambda_length(block_node) - start_line = block_node.loc.begin.line - end_line = block_node.loc.end.line + def offending_selector?(node, selector) + lines = node.multiline? ? :multiline : :single_line - end_line - start_line + 1 + selector == OFFENDING_SELECTORS[:style][style][lines] end + def message(node, selector) + message = (selector == '->') ? METHOD_MESSAGE : LITERAL_MESSAGE + + format(message, message_line_modifier(node)) + end + + def message_line_modifier(node) + case style + when :line_count_dependent + node.multiline? ? 'multiline' : 'single line' + else + 'all' + end + end + def autocorrect(node) block_method, _args = *node selector = block_method.source # Don't autocorrect if this would change the meaning of the code return if selector == '->' && arg_to_unparenthesized_call?(node) lambda do |corrector| - if block_method.source == 'lambda' - autocorrect_old_to_new(corrector, node) + if selector == 'lambda' + autocorrect_method_to_literal(corrector, node) else - autocorrect_new_to_old(corrector, node) + autocorrect_literal_to_method(corrector, node) end end end - def autocorrect_new_to_old(corrector, node) + def autocorrect_literal_to_method(corrector, node) block_method, args = *node # Avoid correcting to `lambdado` by inserting whitespace # if none exists before or after the lambda arguments. if needs_whitespace?(block_method, args, node) corrector.insert_before(node.loc.begin, ' ') @@ -79,10 +147,10 @@ return if args.children.empty? arg_str = " |#{lambda_arg_string(args)}|" corrector.insert_after(node.loc.begin, arg_str) end - def autocorrect_old_to_new(corrector, node) + def autocorrect_method_to_literal(corrector, node) block_method, args = *node corrector.replace(block_method.source_range, '->') return if args.children.empty? arg_str = "(#{lambda_arg_string(args)})"