rubocop 1.31.1 → 1.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|