forked from rubocop/rubocop
/
method_def_parentheses.rb
169 lines (154 loc) · 5.13 KB
/
method_def_parentheses.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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# frozen_string_literal: true
module RuboCop
module Cop
module Style
# This cop checks for parentheses around the arguments in method
# definitions. Both instance and class/singleton methods are checked.
#
# This cop does not consider endless methods, since parentheses are
# always required for them.
#
# @example EnforcedStyle: require_parentheses (default)
# # The `require_parentheses` style requires method definitions
# # to always use parentheses
#
# # bad
# def bar num1, num2
# num1 + num2
# end
#
# def foo descriptive_var_name,
# another_descriptive_var_name,
# last_descriptive_var_name
# do_something
# end
#
# # good
# def bar(num1, num2)
# num1 + num2
# end
#
# def foo(descriptive_var_name,
# another_descriptive_var_name,
# last_descriptive_var_name)
# do_something
# end
#
# @example EnforcedStyle: require_no_parentheses
# # The `require_no_parentheses` style requires method definitions
# # to never use parentheses
#
# # bad
# def bar(num1, num2)
# num1 + num2
# end
#
# def foo(descriptive_var_name,
# another_descriptive_var_name,
# last_descriptive_var_name)
# do_something
# end
#
# # good
# def bar num1, num2
# num1 + num2
# end
#
# def foo descriptive_var_name,
# another_descriptive_var_name,
# last_descriptive_var_name
# do_something
# end
#
# @example EnforcedStyle: require_no_parentheses_except_multiline
# # The `require_no_parentheses_except_multiline` style prefers no
# # parentheses when method definition arguments fit on single line,
# # but prefers parentheses when arguments span multiple lines.
#
# # bad
# def bar(num1, num2)
# num1 + num2
# end
#
# def foo descriptive_var_name,
# another_descriptive_var_name,
# last_descriptive_var_name
# do_something
# end
#
# # good
# def bar num1, num2
# num1 + num2
# end
#
# def foo(descriptive_var_name,
# another_descriptive_var_name,
# last_descriptive_var_name)
# do_something
# end
class MethodDefParentheses < Base
include ConfigurableEnforcedStyle
include RangeHelp
extend AutoCorrector
MSG_PRESENT = 'Use def without parentheses.'
MSG_MISSING = 'Use def with parentheses when there are parameters.'
def on_def(node)
return if forced_parentheses?(node)
args = node.arguments
if require_parentheses?(args)
if arguments_without_parentheses?(node)
missing_parentheses(node)
else
correct_style_detected
end
elsif parentheses?(args)
unwanted_parentheses(args)
else
correct_style_detected
end
end
alias on_defs on_def
private
def correct_arguments(arg_node, corrector)
corrector.replace(arg_node.loc.begin, ' ')
corrector.remove(arg_node.loc.end)
end
def correct_definition(def_node, corrector)
arguments_range = def_node.arguments.source_range
args_with_space = range_with_surrounding_space(range: arguments_range, side: :left)
leading_space = range_between(args_with_space.begin_pos, arguments_range.begin_pos)
corrector.replace(leading_space, '(')
corrector.insert_after(arguments_range, ')')
end
def forced_parentheses?(node)
# Regardless of style, parentheses are necessary for:
# 1. Endless methods
# 2. Argument lists containing a `forward-arg` (`...`)
# Removing the parens would be a syntax error here.
node.endless? || node.arguments.any?(&:forward_arg_type?)
end
def require_parentheses?(args)
style == :require_parentheses ||
(style == :require_no_parentheses_except_multiline && args.multiline?)
end
def arguments_without_parentheses?(node)
node.arguments? && !parentheses?(node.arguments)
end
def missing_parentheses(node)
location = node.arguments.source_range
add_offense(location, message: MSG_MISSING) do |corrector|
correct_definition(node, corrector)
unexpected_style_detected 'require_no_parentheses'
end
end
def unwanted_parentheses(args)
add_offense(args, message: MSG_PRESENT) do |corrector|
# offense is registered on args node when parentheses are unwanted
correct_arguments(args, corrector)
unexpected_style_detected 'require_parentheses'
end
end
end
end
end
end