forked from rubocop/rubocop
-
Notifications
You must be signed in to change notification settings - Fork 0
/
unexpected_block_arity.rb
90 lines (77 loc) · 2.71 KB
/
unexpected_block_arity.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# frozen_string_literal: true
module RuboCop
module Cop
module Lint
# This cop checks for a block that is known to need more positional
# block arguments than are given (by default this is configured for
# `Enumerable` methods needing 2 arguments). Optional arguments are allowed,
# although they don't generally make sense as the default value will
# be used. Blocks that have no receiver, or take splatted arguments
# (ie. `*args`) are always accepted.
#
# Keyword arguments (including `**kwargs`) do not get counted towards
# this, as they are not used by the methods in question.
#
# Method names and their expected arity can be configured like this:
#
# [source,yaml]
# ----
# Methods:
# inject: 2
# reduce: 2
# ----
#
# @safety
# This cop matches for method names only and hence cannot tell apart
# methods with same name in different classes, which may lead to a
# false positive.
#
# @example
# # bad
# values.reduce {}
# values.min { |a| a }
# values.sort { |a; b| a + b }
#
# # good
# values.reduce { |memo, obj| memo << obj }
# values.min { |a, b| a <=> b }
# values.sort { |*x| x[0] <=> x[1] }
#
class UnexpectedBlockArity < Base
MSG = '`%<method>s` expects at least %<expected>i positional arguments, got %<actual>i.'
def on_block(node)
return if acceptable?(node)
expected = expected_arity(node.method_name)
actual = arg_count(node)
return if actual >= expected
message = format(MSG, method: node.method_name, expected: expected, actual: actual)
add_offense(node, message: message)
end
alias on_numblock on_block
private
def methods
cop_config.fetch('Methods', [])
end
def acceptable?(node)
!(included_method?(node.method_name) && node.receiver)
end
def included_method?(name)
methods.key?(name.to_s)
end
def expected_arity(method)
cop_config['Methods'][method.to_s]
end
def arg_count(node)
return node.children[1] if node.numblock_type? # the maximum numbered param for the block
# Only `arg`, `optarg` and `mlhs` (destructuring) count as arguments that
# can be used. Keyword arguments are not used for these methods so are
# ignored.
node.arguments.count do |arg|
return Float::INFINITY if arg.restarg_type?
arg.arg_type? || arg.optarg_type? || arg.mlhs_type?
end
end
end
end
end
end