rubocop 1.31.1 → 1.33.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/config/default.yml +68 -16
- data/config/obsoletion.yml +23 -1
- data/lib/rubocop/cache_config.rb +29 -0
- data/lib/rubocop/cli/command/auto_genenerate_config.rb +2 -2
- data/lib/rubocop/cli/command/init_dotfile.rb +1 -1
- data/lib/rubocop/cli.rb +1 -0
- data/lib/rubocop/config.rb +1 -1
- data/lib/rubocop/config_finder.rb +68 -0
- data/lib/rubocop/config_loader.rb +5 -45
- data/lib/rubocop/config_loader_resolver.rb +1 -1
- data/lib/rubocop/config_obsoletion/changed_parameter.rb +5 -0
- data/lib/rubocop/config_obsoletion/parameter_rule.rb +4 -0
- data/lib/rubocop/config_obsoletion.rb +7 -2
- data/lib/rubocop/cop/base.rb +1 -1
- data/lib/rubocop/cop/generator.rb +4 -0
- data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +60 -0
- data/lib/rubocop/cop/internal_affairs.rb +1 -0
- data/lib/rubocop/cop/layout/block_end_newline.rb +32 -5
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +6 -1
- data/lib/rubocop/cop/layout/first_array_element_indentation.rb +4 -3
- data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +3 -3
- data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +57 -13
- data/lib/rubocop/cop/layout/line_length.rb +2 -0
- data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +4 -1
- data/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb +45 -0
- data/lib/rubocop/cop/lint/ambiguous_block_association.rb +26 -6
- data/lib/rubocop/cop/lint/debugger.rb +11 -1
- data/lib/rubocop/cop/lint/deprecated_class_methods.rb +10 -4
- data/lib/rubocop/cop/lint/empty_conditional_body.rb +60 -1
- data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +60 -25
- data/lib/rubocop/cop/lint/number_conversion.rb +28 -6
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +7 -0
- data/lib/rubocop/cop/lint/require_range_parentheses.rb +57 -0
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +35 -1
- data/lib/rubocop/cop/metrics/abc_size.rb +3 -1
- data/lib/rubocop/cop/metrics/block_length.rb +6 -6
- data/lib/rubocop/cop/metrics/method_length.rb +8 -7
- data/lib/rubocop/cop/mixin/allowed_methods.rb +15 -1
- data/lib/rubocop/cop/mixin/allowed_pattern.rb +9 -1
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +4 -0
- data/lib/rubocop/cop/mixin/comments_help.rb +5 -1
- data/lib/rubocop/cop/mixin/def_node.rb +2 -7
- data/lib/rubocop/cop/mixin/method_complexity.rb +4 -9
- data/lib/rubocop/cop/mixin/multiline_element_indentation.rb +6 -13
- data/lib/rubocop/cop/mixin/percent_array.rb +60 -1
- data/lib/rubocop/cop/naming/predicate_name.rb +30 -1
- data/lib/rubocop/cop/style/block_delimiters.rb +26 -7
- data/lib/rubocop/cop/style/class_and_module_children.rb +4 -4
- data/lib/rubocop/cop/style/class_equality_comparison.rb +50 -3
- data/lib/rubocop/cop/style/empty_else.rb +37 -0
- data/lib/rubocop/cop/style/empty_heredoc.rb +73 -0
- data/lib/rubocop/cop/style/fetch_env_var.rb +10 -177
- data/lib/rubocop/cop/style/format_string_token.rb +25 -6
- data/lib/rubocop/cop/style/hash_except.rb +0 -4
- data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +2 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +5 -1
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +7 -7
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +19 -2
- data/lib/rubocop/cop/style/module_function.rb +2 -2
- data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +9 -0
- data/lib/rubocop/cop/style/numeric_predicate.rb +43 -9
- data/lib/rubocop/cop/style/redundant_condition.rb +19 -4
- data/lib/rubocop/cop/style/redundant_parentheses.rb +2 -1
- data/lib/rubocop/cop/style/redundant_sort.rb +21 -6
- data/lib/rubocop/cop/style/semicolon.rb +27 -3
- data/lib/rubocop/cop/style/symbol_array.rb +2 -3
- data/lib/rubocop/cop/style/symbol_proc.rb +35 -7
- data/lib/rubocop/cop/style/trivial_accessors.rb +3 -0
- data/lib/rubocop/cop/style/word_array.rb +2 -3
- data/lib/rubocop/options.rb +3 -6
- data/lib/rubocop/rake_task.rb +5 -1
- data/lib/rubocop/result_cache.rb +22 -20
- data/lib/rubocop/rspec/shared_contexts.rb +14 -14
- data/lib/rubocop/rspec/support.rb +14 -0
- data/lib/rubocop/runner.rb +4 -0
- data/lib/rubocop/server/cache.rb +33 -1
- data/lib/rubocop/server/cli.rb +19 -2
- data/lib/rubocop/server/client_command/base.rb +1 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +4 -2
- metadata +12 -7
- data/lib/rubocop/cop/mixin/ignored_methods.rb +0 -52
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module InternalAffairs
|
6
|
+
# Check for useless `RESTRICT_ON_SEND`.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# class FooCop
|
11
|
+
# RESTRICT_ON_SEND = %i[bad_method].freeze
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# # good
|
15
|
+
# class FooCop
|
16
|
+
# RESTRICT_ON_SEND = %i[bad_method].freeze
|
17
|
+
# def on_send(node)
|
18
|
+
# # ...
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# # good
|
23
|
+
# class FooCop
|
24
|
+
# RESTRICT_ON_SEND = %i[bad_method].freeze
|
25
|
+
# def after_send(node)
|
26
|
+
# # ...
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
class UselessRestrictOnSend < Base
|
31
|
+
extend AutoCorrector
|
32
|
+
|
33
|
+
MSG = 'Useless `RESTRICT_ON_SEND` is defined.'
|
34
|
+
|
35
|
+
# @!method defined_send_callback?(node)
|
36
|
+
def_node_search :defined_send_callback?, <<~PATTERN
|
37
|
+
{
|
38
|
+
(def {:on_send :after_send} ...)
|
39
|
+
(alias (sym {:on_send :after_send}) _source ...)
|
40
|
+
(send nil? :alias_method {(sym {:on_send :after_send}) (str {"on_send" "after_send"})} _source ...)
|
41
|
+
}
|
42
|
+
PATTERN
|
43
|
+
|
44
|
+
def on_casgn(node)
|
45
|
+
return if !restrict_on_send?(node) || defined_send_callback?(node.parent)
|
46
|
+
|
47
|
+
add_offense(node) do |corrector|
|
48
|
+
corrector.remove(node)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def restrict_on_send?(node)
|
55
|
+
node.name == :RESTRICT_ON_SEND
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -20,3 +20,4 @@ require_relative 'internal_affairs/redundant_method_dispatch_node'
|
|
20
20
|
require_relative 'internal_affairs/style_detected_api_use'
|
21
21
|
require_relative 'internal_affairs/undefined_config'
|
22
22
|
require_relative 'internal_affairs/useless_message_assertion'
|
23
|
+
require_relative 'internal_affairs/useless_restrict_on_send'
|
@@ -36,24 +36,51 @@ module RuboCop
|
|
36
36
|
# If the end is on its own line, there is no offense
|
37
37
|
return if begins_its_line?(node.loc.end)
|
38
38
|
|
39
|
-
|
40
|
-
corrector.replace(delimiter_range(node), "\n#{node.loc.end.source}#{offset(node)}")
|
41
|
-
end
|
39
|
+
register_offense(node)
|
42
40
|
end
|
43
41
|
|
44
42
|
private
|
45
43
|
|
44
|
+
def register_offense(node)
|
45
|
+
add_offense(node.loc.end, message: message(node)) do |corrector|
|
46
|
+
offense_range = offense_range(node)
|
47
|
+
replacement = "\n#{offense_range.source.strip}"
|
48
|
+
|
49
|
+
if (heredoc = last_heredoc_argument(node.body))
|
50
|
+
corrector.remove(offense_range)
|
51
|
+
corrector.insert_after(heredoc.loc.heredoc_end, replacement)
|
52
|
+
else
|
53
|
+
corrector.replace(offense_range, replacement)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
46
58
|
def message(node)
|
47
59
|
format(MSG, line: node.loc.end.line, column: node.loc.end.column + 1)
|
48
60
|
end
|
49
61
|
|
50
|
-
def
|
62
|
+
def last_heredoc_argument(node)
|
63
|
+
return unless (arguments = node&.arguments)
|
64
|
+
|
65
|
+
heredoc = arguments.reverse.detect(&:heredoc?)
|
66
|
+
return heredoc if heredoc
|
67
|
+
|
68
|
+
last_heredoc_argument(node.children.first)
|
69
|
+
end
|
70
|
+
|
71
|
+
def offense_range(node)
|
51
72
|
Parser::Source::Range.new(
|
52
73
|
node.loc.expression.source_buffer,
|
53
74
|
node.children.compact.last.loc.expression.end_pos,
|
54
|
-
node.loc.expression.end_pos
|
75
|
+
end_of_method_chain(node).loc.expression.end_pos
|
55
76
|
)
|
56
77
|
end
|
78
|
+
|
79
|
+
def end_of_method_chain(node)
|
80
|
+
return node unless node.parent&.call_type?
|
81
|
+
|
82
|
+
end_of_method_chain(node.parent)
|
83
|
+
end
|
57
84
|
end
|
58
85
|
end
|
59
86
|
end
|
@@ -153,7 +153,8 @@ module RuboCop
|
|
153
153
|
MSG = 'Indent the first argument one step more than %<base>s.'
|
154
154
|
|
155
155
|
def on_send(node)
|
156
|
-
return if style != :consistent && enforce_first_argument_with_fixed_indentation?
|
156
|
+
return if style != :consistent && enforce_first_argument_with_fixed_indentation? &&
|
157
|
+
!enable_layout_first_method_argument_line_break?
|
157
158
|
return if !node.arguments? || bare_operator?(node) || node.setter_method?
|
158
159
|
|
159
160
|
indent = base_indentation(node) + configured_indentation_width
|
@@ -267,6 +268,10 @@ module RuboCop
|
|
267
268
|
argument_alignment_config['EnforcedStyle'] == 'with_fixed_indentation'
|
268
269
|
end
|
269
270
|
|
271
|
+
def enable_layout_first_method_argument_line_break?
|
272
|
+
config.for_cop('Layout/FirstMethodArgumentLineBreak')['Enabled']
|
273
|
+
end
|
274
|
+
|
270
275
|
def argument_alignment_config
|
271
276
|
config.for_cop('Layout/ArgumentAlignment')
|
272
277
|
end
|
@@ -120,14 +120,15 @@ module RuboCop
|
|
120
120
|
check_first(first_elem, left_bracket, left_parenthesis, 0)
|
121
121
|
end
|
122
122
|
|
123
|
-
check_right_bracket(array_node.loc.end, left_bracket, left_parenthesis)
|
123
|
+
check_right_bracket(array_node.loc.end, first_elem, left_bracket, left_parenthesis)
|
124
124
|
end
|
125
125
|
|
126
|
-
def check_right_bracket(right_bracket, left_bracket, left_parenthesis)
|
126
|
+
def check_right_bracket(right_bracket, first_elem, left_bracket, left_parenthesis)
|
127
127
|
# if the right bracket is on the same line as the last value, accept
|
128
128
|
return if /\S/.match?(right_bracket.source_line[0...right_bracket.column])
|
129
129
|
|
130
|
-
expected_column, indent_base_type = indent_base(left_bracket,
|
130
|
+
expected_column, indent_base_type = indent_base(left_bracket, first_elem,
|
131
|
+
left_parenthesis)
|
131
132
|
@column_delta = expected_column - right_bracket.column
|
132
133
|
return if @column_delta.zero?
|
133
134
|
|
@@ -158,14 +158,14 @@ module RuboCop
|
|
158
158
|
end
|
159
159
|
end
|
160
160
|
|
161
|
-
check_right_brace(hash_node.loc.end, left_brace, left_parenthesis)
|
161
|
+
check_right_brace(hash_node.loc.end, first_pair, left_brace, left_parenthesis)
|
162
162
|
end
|
163
163
|
|
164
|
-
def check_right_brace(right_brace, left_brace, left_parenthesis)
|
164
|
+
def check_right_brace(right_brace, first_pair, left_brace, left_parenthesis)
|
165
165
|
# if the right brace is on the same line as the last value, accept
|
166
166
|
return if /\S/.match?(right_brace.source_line[0...right_brace.column])
|
167
167
|
|
168
|
-
expected_column, indent_base_type = indent_base(left_brace, left_parenthesis)
|
168
|
+
expected_column, indent_base_type = indent_base(left_brace, first_pair, left_parenthesis)
|
169
169
|
@column_delta = expected_column - right_brace.column
|
170
170
|
return if @column_delta.zero?
|
171
171
|
|
@@ -4,9 +4,10 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module Layout
|
6
6
|
# Checks that strings broken over multiple lines (by a backslash) contain
|
7
|
-
# trailing spaces instead of leading spaces
|
7
|
+
# trailing spaces instead of leading spaces (default) or leading spaces
|
8
|
+
# instead of trailing spaces.
|
8
9
|
#
|
9
|
-
# @example
|
10
|
+
# @example EnforcedStyle: trailing (default)
|
10
11
|
# # bad
|
11
12
|
# 'this text contains a lot of' \
|
12
13
|
# ' spaces'
|
@@ -23,18 +24,38 @@ module RuboCop
|
|
23
24
|
# 'this text is too ' \
|
24
25
|
# 'long'
|
25
26
|
#
|
27
|
+
# @example EnforcedStyle: leading
|
28
|
+
# # bad
|
29
|
+
# 'this text contains a lot of ' \
|
30
|
+
# 'spaces'
|
31
|
+
#
|
32
|
+
# # good
|
33
|
+
# 'this text contains a lot of' \
|
34
|
+
# ' spaces'
|
35
|
+
#
|
36
|
+
# # bad
|
37
|
+
# 'this text is too ' \
|
38
|
+
# 'long'
|
39
|
+
#
|
40
|
+
# # good
|
41
|
+
# 'this text is too' \
|
42
|
+
# ' long'
|
26
43
|
class LineContinuationLeadingSpace < Base
|
27
44
|
include RangeHelp
|
28
45
|
|
29
|
-
MSG = 'Move leading spaces to the end of previous line.'
|
30
|
-
|
31
46
|
def on_dstr(node)
|
32
|
-
|
47
|
+
end_of_first_line = node.loc.expression.begin_pos - node.loc.expression.column
|
33
48
|
|
34
49
|
raw_lines(node).each_cons(2) do |raw_line_one, raw_line_two|
|
35
|
-
|
50
|
+
end_of_first_line += raw_line_one.length
|
36
51
|
|
37
|
-
|
52
|
+
next unless continuation?(raw_line_one)
|
53
|
+
|
54
|
+
if enforced_style_leading?
|
55
|
+
investigate_leading_style(raw_line_one, end_of_first_line)
|
56
|
+
else
|
57
|
+
investigate_trailing_style(raw_line_two, end_of_first_line)
|
58
|
+
end
|
38
59
|
end
|
39
60
|
end
|
40
61
|
|
@@ -44,24 +65,47 @@ module RuboCop
|
|
44
65
|
processed_source.raw_source.lines[node.first_line - 1, line_range(node).size]
|
45
66
|
end
|
46
67
|
|
47
|
-
def
|
48
|
-
|
68
|
+
def investigate_leading_style(first_line, end_of_first_line)
|
69
|
+
matches = first_line.match(/(?<trailing_spaces>\s+)(?<ending>['"]\s*\\\n)/)
|
70
|
+
return if matches.nil?
|
49
71
|
|
50
|
-
|
72
|
+
add_offense(leading_offense_range(end_of_first_line, matches))
|
73
|
+
end
|
74
|
+
|
75
|
+
def investigate_trailing_style(second_line, end_of_first_line)
|
76
|
+
matches = second_line.match(/\A(?<beginning>\s*['"])(?<leading_spaces>\s+)/)
|
51
77
|
return if matches.nil?
|
52
78
|
|
53
|
-
add_offense(
|
79
|
+
add_offense(trailing_offense_range(end_of_first_line, matches))
|
54
80
|
end
|
55
81
|
|
56
82
|
def continuation?(line)
|
57
83
|
line.end_with?("\\\n")
|
58
84
|
end
|
59
85
|
|
60
|
-
def
|
61
|
-
|
86
|
+
def leading_offense_range(end_of_first_line, matches)
|
87
|
+
end_pos = end_of_first_line - matches[:ending].length
|
88
|
+
begin_pos = end_pos - matches[:trailing_spaces].length
|
89
|
+
range_between(begin_pos, end_pos)
|
90
|
+
end
|
91
|
+
|
92
|
+
def trailing_offense_range(end_of_first_line, matches)
|
93
|
+
begin_pos = end_of_first_line + matches[:beginning].length
|
62
94
|
end_pos = begin_pos + matches[:leading_spaces].length
|
63
95
|
range_between(begin_pos, end_pos)
|
64
96
|
end
|
97
|
+
|
98
|
+
def message(_range)
|
99
|
+
if enforced_style_leading?
|
100
|
+
'Move trailing spaces to the start of next line.'
|
101
|
+
else
|
102
|
+
'Move leading spaces to the end of previous line.'
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def enforced_style_leading?
|
107
|
+
cop_config['EnforcedStyle'] == 'leading'
|
108
|
+
end
|
65
109
|
end
|
66
110
|
end
|
67
111
|
end
|
@@ -37,6 +37,7 @@ module RuboCop
|
|
37
37
|
# * MultilineHashBraceLayout
|
38
38
|
# * MultilineHashKeyLineBreaks
|
39
39
|
# * MultilineMethodArgumentLineBreaks
|
40
|
+
# * MultilineMethodParameterLineBreaks
|
40
41
|
# * ParameterAlignment
|
41
42
|
#
|
42
43
|
# Together, these cops will pretty print hashes, arrays,
|
@@ -79,6 +80,7 @@ module RuboCop
|
|
79
80
|
alias on_array on_potential_breakable_node
|
80
81
|
alias on_hash on_potential_breakable_node
|
81
82
|
alias on_send on_potential_breakable_node
|
83
|
+
alias on_def on_potential_breakable_node
|
82
84
|
|
83
85
|
def on_new_investigation
|
84
86
|
check_for_breakable_semicolons(processed_source)
|
@@ -6,7 +6,7 @@ module RuboCop
|
|
6
6
|
# Ensures that each argument in a multi-line method call
|
7
7
|
# starts on a separate line.
|
8
8
|
#
|
9
|
-
# NOTE:
|
9
|
+
# NOTE: This cop does not move the first argument, if you want that to
|
10
10
|
# be on a separate line, see `Layout/FirstMethodArgumentLineBreak`.
|
11
11
|
#
|
12
12
|
# @example
|
@@ -22,6 +22,9 @@ module RuboCop
|
|
22
22
|
# b,
|
23
23
|
# c
|
24
24
|
# )
|
25
|
+
#
|
26
|
+
# # good
|
27
|
+
# foo(a, b, c)
|
25
28
|
class MultilineMethodArgumentLineBreaks < Base
|
26
29
|
include MultilineElementLineBreaks
|
27
30
|
extend AutoCorrector
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Layout
|
6
|
+
# Ensures that each parameter in a multi-line method definition
|
7
|
+
# starts on a separate line.
|
8
|
+
#
|
9
|
+
# NOTE: This cop does not move the first argument, if you want that to
|
10
|
+
# be on a separate line, see `Layout/FirstMethodParameterLineBreak`.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
#
|
14
|
+
# # bad
|
15
|
+
# def foo(a, b,
|
16
|
+
# c
|
17
|
+
# )
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# # good
|
21
|
+
# def foo(
|
22
|
+
# a,
|
23
|
+
# b,
|
24
|
+
# c
|
25
|
+
# )
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# # good
|
29
|
+
# def foo(a, b, c)
|
30
|
+
# end
|
31
|
+
class MultilineMethodParameterLineBreaks < Base
|
32
|
+
include MultilineElementLineBreaks
|
33
|
+
extend AutoCorrector
|
34
|
+
|
35
|
+
MSG = 'Each parameter in a multi-line method definition must start on a separate line.'
|
36
|
+
|
37
|
+
def on_def(node)
|
38
|
+
return if node.arguments.empty?
|
39
|
+
|
40
|
+
check_line_breaks(node, node.arguments)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -6,7 +6,8 @@ module RuboCop
|
|
6
6
|
# Checks for ambiguous block association with method
|
7
7
|
# when param passed without parentheses.
|
8
8
|
#
|
9
|
-
# This cop can customize
|
9
|
+
# This cop can customize allowed methods with `AllowedMethods`.
|
10
|
+
# By default, there are no methods to allowed.
|
10
11
|
#
|
11
12
|
# @example
|
12
13
|
#
|
@@ -29,12 +30,30 @@ module RuboCop
|
|
29
30
|
# # Lambda arguments require no disambiguation
|
30
31
|
# foo = ->(bar) { bar.baz }
|
31
32
|
#
|
32
|
-
# @example
|
33
|
+
# @example AllowedMethods: [] (default)
|
34
|
+
#
|
35
|
+
# # bad
|
36
|
+
# expect { do_something }.to change { object.attribute }
|
37
|
+
#
|
38
|
+
# @example AllowedMethods: [change]
|
33
39
|
#
|
34
40
|
# # good
|
35
41
|
# expect { do_something }.to change { object.attribute }
|
42
|
+
#
|
43
|
+
# @example AllowedPatterns: [] (default)
|
44
|
+
#
|
45
|
+
# # bad
|
46
|
+
# expect { do_something }.to change { object.attribute }
|
47
|
+
#
|
48
|
+
# @example AllowedPatterns: [/change/]
|
49
|
+
#
|
50
|
+
# # good
|
51
|
+
# expect { do_something }.to change { object.attribute }
|
52
|
+
# expect { do_something }.to not_change { object.attribute }
|
53
|
+
#
|
36
54
|
class AmbiguousBlockAssociation < Base
|
37
|
-
include
|
55
|
+
include AllowedMethods
|
56
|
+
include AllowedPattern
|
38
57
|
|
39
58
|
MSG = 'Parenthesize the param `%<param>s` to make sure that the ' \
|
40
59
|
'block will be associated with the `%<method>s` method ' \
|
@@ -45,7 +64,7 @@ module RuboCop
|
|
45
64
|
|
46
65
|
return unless ambiguous_block_association?(node)
|
47
66
|
return if node.parenthesized? || node.last_argument.lambda? || node.last_argument.proc? ||
|
48
|
-
|
67
|
+
allowed_method_pattern?(node)
|
49
68
|
|
50
69
|
message = message(node)
|
51
70
|
|
@@ -59,9 +78,10 @@ module RuboCop
|
|
59
78
|
send_node.last_argument.block_type? && !send_node.last_argument.send_node.arguments?
|
60
79
|
end
|
61
80
|
|
62
|
-
def
|
81
|
+
def allowed_method_pattern?(node)
|
63
82
|
node.assignment? || node.operator_method? || node.method?(:[]) ||
|
64
|
-
|
83
|
+
allowed_method?(node.last_argument.method_name) ||
|
84
|
+
matches_allowed_pattern?(node.last_argument.method_name)
|
65
85
|
end
|
66
86
|
|
67
87
|
def message(send_node)
|
@@ -15,9 +15,19 @@ module RuboCop
|
|
15
15
|
# [source,yaml]
|
16
16
|
# ----
|
17
17
|
# Lint/Debugger:
|
18
|
-
#
|
18
|
+
# DebuggerMethods:
|
19
|
+
# WebConsole: ~
|
19
20
|
# ----
|
20
21
|
#
|
22
|
+
# You can also add your own methods by adding a new category:
|
23
|
+
#
|
24
|
+
# [source,yaml]
|
25
|
+
# ----
|
26
|
+
# Lint/Debugger:
|
27
|
+
# DebuggerMethods:
|
28
|
+
# MyDebugger:
|
29
|
+
# MyDebugger.debug_this
|
30
|
+
# ----
|
21
31
|
#
|
22
32
|
# @example
|
23
33
|
#
|
@@ -8,22 +8,22 @@ module RuboCop
|
|
8
8
|
# @example
|
9
9
|
#
|
10
10
|
# # bad
|
11
|
-
#
|
12
11
|
# File.exists?(some_path)
|
13
12
|
# Dir.exists?(some_path)
|
14
13
|
# iterator?
|
15
14
|
# ENV.freeze # Calling `Env.freeze` raises `TypeError` since Ruby 2.7.
|
15
|
+
# ENV.clone
|
16
|
+
# ENV.dup # Calling `Env.dup` raises `TypeError` since Ruby 3.1.
|
16
17
|
# Socket.gethostbyname(host)
|
17
18
|
# Socket.gethostbyaddr(host)
|
18
19
|
#
|
19
|
-
# @example
|
20
|
-
#
|
21
20
|
# # good
|
22
|
-
#
|
23
21
|
# File.exist?(some_path)
|
24
22
|
# Dir.exist?(some_path)
|
25
23
|
# block_given?
|
26
24
|
# ENV # `ENV.freeze` cannot prohibit changes to environment variables.
|
25
|
+
# ENV.to_h
|
26
|
+
# ENV.to_h # `ENV.dup` cannot dup `ENV`, use `ENV.to_h` to get a copy of `ENV` as a hash.
|
27
27
|
# Addrinfo.getaddrinfo(nodename, service)
|
28
28
|
# Addrinfo.tcp(host, port).getnameinfo
|
29
29
|
class DeprecatedClassMethods < Base
|
@@ -111,6 +111,12 @@ module RuboCop
|
|
111
111
|
DeprecatedClassMethod.new(:freeze, class_constant: :ENV) =>
|
112
112
|
Replacement.new(nil, class_constant: :ENV),
|
113
113
|
|
114
|
+
DeprecatedClassMethod.new(:clone, class_constant: :ENV) =>
|
115
|
+
Replacement.new(:to_h, class_constant: :ENV),
|
116
|
+
|
117
|
+
DeprecatedClassMethod.new(:dup, class_constant: :ENV) =>
|
118
|
+
Replacement.new(:to_h, class_constant: :ENV),
|
119
|
+
|
114
120
|
DeprecatedClassMethod.new(:gethostbyaddr, class_constant: :Socket, correctable: false) =>
|
115
121
|
Replacement.new(:getnameinfo, class_constant: :Addrinfo, instance_method: true),
|
116
122
|
|
@@ -4,6 +4,9 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module Lint
|
6
6
|
# Checks for the presence of `if`, `elsif` and `unless` branches without a body.
|
7
|
+
#
|
8
|
+
# NOTE: empty `else` branches are handled by `Style/EmptyElse`.
|
9
|
+
#
|
7
10
|
# @example
|
8
11
|
# # bad
|
9
12
|
# if condition
|
@@ -53,7 +56,9 @@ module RuboCop
|
|
53
56
|
# end
|
54
57
|
#
|
55
58
|
class EmptyConditionalBody < Base
|
59
|
+
extend AutoCorrector
|
56
60
|
include CommentsHelp
|
61
|
+
include RangeHelp
|
57
62
|
|
58
63
|
MSG = 'Avoid `%<keyword>s` branches without a body.'
|
59
64
|
|
@@ -61,7 +66,61 @@ module RuboCop
|
|
61
66
|
return if node.body
|
62
67
|
return if cop_config['AllowComments'] && contains_comments?(node)
|
63
68
|
|
64
|
-
add_offense(node, message: format(MSG, keyword: node.keyword))
|
69
|
+
add_offense(node, message: format(MSG, keyword: node.keyword)) do |corrector|
|
70
|
+
autocorrect(corrector, node)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def autocorrect(corrector, node)
|
77
|
+
remove_comments(corrector, node)
|
78
|
+
remove_empty_branch(corrector, node)
|
79
|
+
correct_other_branches(corrector, node)
|
80
|
+
end
|
81
|
+
|
82
|
+
def remove_comments(corrector, node)
|
83
|
+
comments_in_range(node).each do |comment|
|
84
|
+
range = range_by_whole_lines(comment.loc.expression, include_final_newline: true)
|
85
|
+
corrector.remove(range)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def remove_empty_branch(corrector, node)
|
90
|
+
corrector.remove(deletion_range(branch_range(node)))
|
91
|
+
end
|
92
|
+
|
93
|
+
def correct_other_branches(corrector, node)
|
94
|
+
return unless (node.if? || node.unless?) && node.else_branch
|
95
|
+
|
96
|
+
if node.else_branch.if_type?
|
97
|
+
# Replace an orphaned `elsif` with `if`
|
98
|
+
corrector.replace(node.else_branch.loc.keyword, 'if')
|
99
|
+
else
|
100
|
+
# Flip orphaned `else`
|
101
|
+
corrector.replace(node.loc.else, "#{node.inverse_keyword} #{node.condition.source}")
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def branch_range(node)
|
106
|
+
if node.loc.else
|
107
|
+
node.source_range.with(end_pos: node.loc.else.begin_pos - 1)
|
108
|
+
else
|
109
|
+
node.source_range
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def deletion_range(range)
|
114
|
+
# Collect a range between the start of the `if` node and the next relevant node,
|
115
|
+
# including final new line.
|
116
|
+
# Based on `RangeHelp#range_by_whole_lines` but allows the `if` to not start
|
117
|
+
# on the first column.
|
118
|
+
buffer = @processed_source.buffer
|
119
|
+
|
120
|
+
last_line = buffer.source_line(range.last_line)
|
121
|
+
end_offset = last_line.length - range.last_column + 1
|
122
|
+
|
123
|
+
range.adjust(end_pos: end_offset).intersect(buffer.source_range)
|
65
124
|
end
|
66
125
|
end
|
67
126
|
end
|