rubocop 1.30.1 → 1.31.1
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 +1 -1
- data/config/default.yml +41 -8
- data/config/obsoletion.yml +2 -0
- data/exe/rubocop +15 -7
- data/lib/rubocop/cli/command/auto_genenerate_config.rb +1 -1
- data/lib/rubocop/cli/command/suggest_extensions.rb +3 -3
- data/lib/rubocop/config.rb +4 -0
- data/lib/rubocop/config_loader.rb +1 -0
- data/lib/rubocop/config_validator.rb +3 -3
- data/lib/rubocop/cop/base.rb +4 -0
- data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -1
- data/lib/rubocop/cop/bundler/gem_filename.rb +4 -4
- data/lib/rubocop/cop/bundler/ordered_gems.rb +2 -2
- data/lib/rubocop/cop/corrector.rb +2 -2
- data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +3 -3
- data/lib/rubocop/cop/correctors/unused_arg_corrector.rb +1 -1
- data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +31 -16
- data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +1 -1
- data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
- data/lib/rubocop/cop/gemspec/require_mfa.rb +20 -20
- data/lib/rubocop/cop/generator.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -5
- data/lib/rubocop/cop/internal_affairs/redundant_location_argument.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/redundant_message_argument.rb +1 -1
- data/lib/rubocop/cop/layout/empty_comment.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +25 -4
- data/lib/rubocop/cop/layout/first_array_element_indentation.rb +17 -11
- data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +49 -10
- data/lib/rubocop/cop/layout/initial_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +68 -0
- data/lib/rubocop/cop/layout/line_continuation_spacing.rb +130 -0
- data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -1
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +2 -2
- data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -2
- data/lib/rubocop/cop/layout/space_around_block_parameters.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_operators.rb +1 -1
- data/lib/rubocop/cop/layout/space_before_block_braces.rb +1 -1
- data/lib/rubocop/cop/layout/space_before_first_arg.rb +1 -1
- data/lib/rubocop/cop/layout/space_inside_block_braces.rb +5 -3
- data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -1
- data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +51 -0
- data/lib/rubocop/cop/lint/interpolation_check.rb +1 -1
- data/lib/rubocop/cop/lint/literal_as_condition.rb +5 -0
- data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +122 -0
- data/lib/rubocop/cop/lint/number_conversion.rb +3 -3
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +5 -5
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_require_statement.rb +1 -1
- data/lib/rubocop/cop/lint/regexp_as_condition.rb +2 -2
- data/lib/rubocop/cop/lint/struct_new_override.rb +2 -2
- data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -1
- data/lib/rubocop/cop/lint/useless_else_without_rescue.rb +44 -0
- data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
- data/lib/rubocop/cop/mixin/multiline_element_indentation.rb +41 -12
- data/lib/rubocop/cop/mixin/range_help.rb +7 -3
- data/lib/rubocop/cop/style/accessor_grouping.rb +1 -1
- data/lib/rubocop/cop/style/arguments_forwarding.rb +1 -1
- data/lib/rubocop/cop/style/block_delimiters.rb +4 -2
- data/lib/rubocop/cop/style/commented_keyword.rb +1 -1
- data/lib/rubocop/cop/style/conditional_assignment.rb +1 -0
- data/lib/rubocop/cop/style/empty_method.rb +16 -1
- data/lib/rubocop/cop/style/encoding.rb +1 -1
- data/lib/rubocop/cop/style/explicit_block_argument.rb +1 -1
- data/lib/rubocop/cop/style/format_string_token.rb +48 -17
- data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +1 -1
- data/lib/rubocop/cop/style/guard_clause.rb +8 -6
- data/lib/rubocop/cop/style/hash_as_last_array_item.rb +1 -1
- data/lib/rubocop/cop/style/hash_except.rb +88 -8
- data/lib/rubocop/cop/style/hash_syntax.rb +2 -2
- data/lib/rubocop/cop/style/implicit_runtime_error.rb +2 -2
- data/lib/rubocop/cop/style/keyword_parameters_order.rb +1 -1
- data/lib/rubocop/cop/style/line_end_concatenation.rb +1 -1
- data/lib/rubocop/cop/style/map_to_hash.rb +1 -1
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -2
- data/lib/rubocop/cop/style/multiline_if_modifier.rb +2 -2
- data/lib/rubocop/cop/style/multiline_if_then.rb +1 -1
- data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +1 -3
- data/lib/rubocop/cop/style/multiline_method_signature.rb +1 -1
- data/lib/rubocop/cop/style/multiline_ternary_operator.rb +1 -1
- data/lib/rubocop/cop/style/multiline_when_then.rb +1 -3
- data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +1 -1
- data/lib/rubocop/cop/style/nested_ternary_operator.rb +19 -7
- data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
- data/lib/rubocop/cop/style/not.rb +1 -1
- data/lib/rubocop/cop/style/redundant_argument.rb +1 -1
- data/lib/rubocop/cop/style/redundant_return.rb +1 -1
- data/lib/rubocop/cop/style/redundant_self_assignment.rb +1 -1
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +2 -2
- data/lib/rubocop/cop/style/struct_inheritance.rb +2 -2
- data/lib/rubocop/cop/style/swap_values.rb +1 -1
- data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
- data/lib/rubocop/cop/style/unpack_first.rb +1 -1
- data/lib/rubocop/cop/util.rb +1 -1
- data/lib/rubocop/formatter/formatter_set.rb +20 -19
- data/lib/rubocop/formatter/git_hub_actions_formatter.rb +15 -2
- data/lib/rubocop/formatter/html_formatter.rb +0 -1
- data/lib/rubocop/formatter/offense_count_formatter.rb +2 -0
- data/lib/rubocop/formatter/simple_text_formatter.rb +6 -7
- data/lib/rubocop/formatter.rb +31 -0
- data/lib/rubocop/options.rb +24 -1
- data/lib/rubocop/rake_task.rb +30 -9
- data/lib/rubocop/server/cache.rb +109 -0
- data/lib/rubocop/server/cli.rb +104 -0
- data/lib/rubocop/server/client_command/base.rb +44 -0
- data/lib/rubocop/server/client_command/exec.rb +59 -0
- data/lib/rubocop/server/client_command/restart.rb +25 -0
- data/lib/rubocop/server/client_command/start.rb +43 -0
- data/lib/rubocop/server/client_command/status.rb +28 -0
- data/lib/rubocop/server/client_command/stop.rb +31 -0
- data/lib/rubocop/server/client_command.rb +26 -0
- data/lib/rubocop/server/core.rb +79 -0
- data/lib/rubocop/server/errors.rb +23 -0
- data/lib/rubocop/server/helper.rb +34 -0
- data/lib/rubocop/server/server_command/base.rb +50 -0
- data/lib/rubocop/server/server_command/exec.rb +34 -0
- data/lib/rubocop/server/server_command/stop.rb +24 -0
- data/lib/rubocop/server/server_command.rb +21 -0
- data/lib/rubocop/server/socket_reader.rb +65 -0
- data/lib/rubocop/server.rb +53 -0
- data/lib/rubocop/version.rb +15 -8
- data/lib/rubocop.rb +7 -26
- metadata +42 -5
- data/lib/rubocop/cop/gemspec/date_assignment.rb +0 -49
@@ -32,6 +32,14 @@ module RuboCop
|
|
32
32
|
# and_in_a_method_call({
|
33
33
|
# no: :difference
|
34
34
|
# })
|
35
|
+
# takes_multi_pairs_hash(x: {
|
36
|
+
# a: 1,
|
37
|
+
# b: 2
|
38
|
+
# },
|
39
|
+
# y: {
|
40
|
+
# c: 1,
|
41
|
+
# d: 2
|
42
|
+
# })
|
35
43
|
#
|
36
44
|
# # good
|
37
45
|
# special_inside_parentheses
|
@@ -41,6 +49,14 @@ module RuboCop
|
|
41
49
|
# but_in_a_method_call({
|
42
50
|
# its_like: :this
|
43
51
|
# })
|
52
|
+
# takes_multi_pairs_hash(x: {
|
53
|
+
# a: 1,
|
54
|
+
# b: 2
|
55
|
+
# },
|
56
|
+
# y: {
|
57
|
+
# c: 1,
|
58
|
+
# d: 2
|
59
|
+
# })
|
44
60
|
#
|
45
61
|
# @example EnforcedStyle: consistent
|
46
62
|
# # The `consistent` style enforces that the first key in a hash
|
@@ -64,6 +80,7 @@ module RuboCop
|
|
64
80
|
# no: :difference
|
65
81
|
# })
|
66
82
|
#
|
83
|
+
#
|
67
84
|
# @example EnforcedStyle: align_braces
|
68
85
|
# # The `align_brackets` style enforces that the opening and closing
|
69
86
|
# # braces are indented to the same position.
|
@@ -72,11 +89,27 @@ module RuboCop
|
|
72
89
|
# and_now_for_something = {
|
73
90
|
# completely: :different
|
74
91
|
# }
|
92
|
+
# takes_multi_pairs_hash(x: {
|
93
|
+
# a: 1,
|
94
|
+
# b: 2
|
95
|
+
# },
|
96
|
+
# y: {
|
97
|
+
# c: 1,
|
98
|
+
# d: 2
|
99
|
+
# })
|
75
100
|
#
|
76
101
|
# # good
|
77
102
|
# and_now_for_something = {
|
78
103
|
# completely: :different
|
79
104
|
# }
|
105
|
+
# takes_multi_pairs_hash(x: {
|
106
|
+
# a: 1,
|
107
|
+
# b: 2
|
108
|
+
# },
|
109
|
+
# y: {
|
110
|
+
# c: 1,
|
111
|
+
# d: 2
|
112
|
+
# })
|
80
113
|
class FirstHashElementIndentation < Base
|
81
114
|
include Alignment
|
82
115
|
include ConfigurableEnforcedStyle
|
@@ -132,11 +165,11 @@ module RuboCop
|
|
132
165
|
# if the right brace is on the same line as the last value, accept
|
133
166
|
return if /\S/.match?(right_brace.source_line[0...right_brace.column])
|
134
167
|
|
135
|
-
expected_column =
|
168
|
+
expected_column, indent_base_type = indent_base(left_brace, left_parenthesis)
|
136
169
|
@column_delta = expected_column - right_brace.column
|
137
170
|
return if @column_delta.zero?
|
138
171
|
|
139
|
-
message = message_for_right_brace(
|
172
|
+
message = message_for_right_brace(indent_base_type)
|
140
173
|
add_offense(right_brace, message: message) do |corrector|
|
141
174
|
autocorrect(corrector, right_brace)
|
142
175
|
end
|
@@ -155,11 +188,14 @@ module RuboCop
|
|
155
188
|
end
|
156
189
|
|
157
190
|
# Returns the description of what the correct indentation is based on.
|
158
|
-
def base_description(
|
159
|
-
|
191
|
+
def base_description(indent_base_type)
|
192
|
+
case indent_base_type
|
193
|
+
when :left_brace_or_bracket
|
160
194
|
'the position of the opening brace'
|
161
|
-
|
195
|
+
when :first_colmn_after_left_parenthesis
|
162
196
|
'the first position after the preceding left parenthesis'
|
197
|
+
when :parent_hash_key
|
198
|
+
'the parent hash key'
|
163
199
|
else
|
164
200
|
'the start of the line where the left curly brace is'
|
165
201
|
end
|
@@ -173,15 +209,18 @@ module RuboCop
|
|
173
209
|
)
|
174
210
|
end
|
175
211
|
|
176
|
-
def message_for_right_brace(
|
177
|
-
|
212
|
+
def message_for_right_brace(indent_base_type)
|
213
|
+
case indent_base_type
|
214
|
+
when :left_brace_or_bracket
|
178
215
|
'Indent the right brace the same as the left brace.'
|
179
|
-
|
216
|
+
when :first_colmn_after_left_parenthesis
|
180
217
|
'Indent the right brace the same as the first position ' \
|
181
|
-
|
218
|
+
'after the preceding left parenthesis.'
|
219
|
+
when :parent_hash_key
|
220
|
+
'Indent the right brace the same as the parent hash key.'
|
182
221
|
else
|
183
222
|
'Indent the right brace the same as the start of the line ' \
|
184
|
-
|
223
|
+
'where the left brace is.'
|
185
224
|
end
|
186
225
|
end
|
187
226
|
|
@@ -41,7 +41,7 @@ module RuboCop
|
|
41
41
|
return unless token
|
42
42
|
return if token.column.zero?
|
43
43
|
|
44
|
-
space_range = range_with_surrounding_space(
|
44
|
+
space_range = range_with_surrounding_space(token.pos, side: :left, newlines: false)
|
45
45
|
# If the file starts with a byte order mark (BOM), the column can be
|
46
46
|
# non-zero, but then we find out here if there's no space to the left
|
47
47
|
# of the first token.
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Layout
|
6
|
+
# Checks that strings broken over multiple lines (by a backslash) contain
|
7
|
+
# trailing spaces instead of leading spaces.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# 'this text contains a lot of' \
|
12
|
+
# ' spaces'
|
13
|
+
#
|
14
|
+
# # good
|
15
|
+
# 'this text contains a lot of ' \
|
16
|
+
# 'spaces'
|
17
|
+
#
|
18
|
+
# # bad
|
19
|
+
# 'this text is too' \
|
20
|
+
# ' long'
|
21
|
+
#
|
22
|
+
# # good
|
23
|
+
# 'this text is too ' \
|
24
|
+
# 'long'
|
25
|
+
#
|
26
|
+
class LineContinuationLeadingSpace < Base
|
27
|
+
include RangeHelp
|
28
|
+
|
29
|
+
MSG = 'Move leading spaces to the end of previous line.'
|
30
|
+
|
31
|
+
def on_dstr(node)
|
32
|
+
range_start = node.loc.expression.begin_pos - node.loc.expression.column
|
33
|
+
|
34
|
+
raw_lines(node).each_cons(2) do |raw_line_one, raw_line_two|
|
35
|
+
range_start += raw_line_one.length
|
36
|
+
|
37
|
+
investigate(raw_line_one, raw_line_two, range_start)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def raw_lines(node)
|
44
|
+
processed_source.raw_source.lines[node.first_line - 1, line_range(node).size]
|
45
|
+
end
|
46
|
+
|
47
|
+
def investigate(first_line, second_line, range_start)
|
48
|
+
return unless continuation?(first_line)
|
49
|
+
|
50
|
+
matches = second_line.match(/\A(?<indent>\s*['"])(?<leading_spaces>\s+)/)
|
51
|
+
return if matches.nil?
|
52
|
+
|
53
|
+
add_offense(offense_range(range_start, matches))
|
54
|
+
end
|
55
|
+
|
56
|
+
def continuation?(line)
|
57
|
+
line.end_with?("\\\n")
|
58
|
+
end
|
59
|
+
|
60
|
+
def offense_range(range_start, matches)
|
61
|
+
begin_pos = range_start + matches[:indent].length
|
62
|
+
end_pos = begin_pos + matches[:leading_spaces].length
|
63
|
+
range_between(begin_pos, end_pos)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Layout
|
6
|
+
# Checks that the backslash of a line continuation is separated from
|
7
|
+
# preceding text by exactly one space (default) or zero spaces.
|
8
|
+
#
|
9
|
+
# @example EnforcedStyle: space (default)
|
10
|
+
# # bad
|
11
|
+
# 'a'\
|
12
|
+
# 'b' \
|
13
|
+
# 'c'
|
14
|
+
#
|
15
|
+
# # good
|
16
|
+
# 'a' \
|
17
|
+
# 'b' \
|
18
|
+
# 'c'
|
19
|
+
#
|
20
|
+
# @example EnforcedStyle: no_space
|
21
|
+
# # bad
|
22
|
+
# 'a' \
|
23
|
+
# 'b' \
|
24
|
+
# 'c'
|
25
|
+
#
|
26
|
+
# # good
|
27
|
+
# 'a'\
|
28
|
+
# 'b'\
|
29
|
+
# 'c'
|
30
|
+
class LineContinuationSpacing < Base
|
31
|
+
include RangeHelp
|
32
|
+
extend AutoCorrector
|
33
|
+
|
34
|
+
def on_new_investigation
|
35
|
+
last_line = last_line(processed_source)
|
36
|
+
|
37
|
+
@ignored_ranges = string_literal_ranges(processed_source.ast) +
|
38
|
+
comment_ranges(processed_source.comments)
|
39
|
+
|
40
|
+
processed_source.raw_source.lines.each_with_index do |line, index|
|
41
|
+
break if index >= last_line
|
42
|
+
|
43
|
+
line_number = index + 1
|
44
|
+
investigate(line, line_number)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def investigate(line, line_number)
|
51
|
+
offensive_spacing = find_offensive_spacing(line)
|
52
|
+
return unless offensive_spacing
|
53
|
+
|
54
|
+
range = source_range(
|
55
|
+
processed_source.buffer,
|
56
|
+
line_number,
|
57
|
+
line.length - offensive_spacing.length - 1,
|
58
|
+
offensive_spacing.length
|
59
|
+
)
|
60
|
+
|
61
|
+
return if ignore_range?(range)
|
62
|
+
|
63
|
+
add_offense(range) { |corrector| autocorrect(corrector, range) }
|
64
|
+
end
|
65
|
+
|
66
|
+
def find_offensive_spacing(line)
|
67
|
+
if no_space_style?
|
68
|
+
line[/\s+\\$/, 0]
|
69
|
+
elsif space_style?
|
70
|
+
line[/((?<!\s)|\s{2,})\\$/, 0]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def message(_range)
|
75
|
+
if no_space_style?
|
76
|
+
'Use zero spaces in front of backslash.'
|
77
|
+
elsif space_style?
|
78
|
+
'Use one space in front of backslash.'
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def autocorrect(corrector, range)
|
83
|
+
correction = if no_space_style?
|
84
|
+
'\\'
|
85
|
+
elsif space_style?
|
86
|
+
' \\'
|
87
|
+
end
|
88
|
+
corrector.replace(range, correction)
|
89
|
+
end
|
90
|
+
|
91
|
+
def string_literal_ranges(ast)
|
92
|
+
# which lines start inside a string literal?
|
93
|
+
return [] if ast.nil?
|
94
|
+
|
95
|
+
ast.each_node(:str, :dstr).each_with_object(Set.new) do |str, ranges|
|
96
|
+
loc = str.location
|
97
|
+
|
98
|
+
if str.heredoc?
|
99
|
+
ranges << loc.heredoc_body
|
100
|
+
elsif loc.respond_to?(:begin) && loc.begin
|
101
|
+
ranges << loc.expression
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def comment_ranges(comments)
|
107
|
+
comments.map(&:loc).map(&:expression)
|
108
|
+
end
|
109
|
+
|
110
|
+
def last_line(processed_source)
|
111
|
+
last_token = processed_source.tokens.last
|
112
|
+
|
113
|
+
last_token ? last_token.line : processed_source.lines.length
|
114
|
+
end
|
115
|
+
|
116
|
+
def ignore_range?(backtick_range)
|
117
|
+
@ignored_ranges.any? { |range| range.contains?(backtick_range) }
|
118
|
+
end
|
119
|
+
|
120
|
+
def no_space_style?
|
121
|
+
cop_config['EnforcedStyle'] == 'no_space'
|
122
|
+
end
|
123
|
+
|
124
|
+
def space_style?
|
125
|
+
cop_config['EnforcedStyle'] == 'space'
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -56,8 +56,8 @@ module RuboCop
|
|
56
56
|
return unless style == :aligned && cop_config['IndentationWidth']
|
57
57
|
|
58
58
|
raise ValidationError,
|
59
|
-
'The `Layout/MultilineMethodCallIndentation`' \
|
60
|
-
'
|
59
|
+
'The `Layout/MultilineMethodCallIndentation` ' \
|
60
|
+
'cop only accepts an `IndentationWidth` ' \
|
61
61
|
'configuration parameter when ' \
|
62
62
|
'`EnforcedStyle` is `indented`.'
|
63
63
|
end
|
@@ -57,8 +57,8 @@ module RuboCop
|
|
57
57
|
def validate_config
|
58
58
|
return unless style == :aligned && cop_config['IndentationWidth']
|
59
59
|
|
60
|
-
raise ValidationError, 'The `Layout/MultilineOperationIndentation`' \
|
61
|
-
'
|
60
|
+
raise ValidationError, 'The `Layout/MultilineOperationIndentation` ' \
|
61
|
+
'cop only accepts an `IndentationWidth` ' \
|
62
62
|
'configuration parameter when ' \
|
63
63
|
'`EnforcedStyle` is `indented`.'
|
64
64
|
end
|
@@ -127,7 +127,7 @@ module RuboCop
|
|
127
127
|
|
128
128
|
expr = arg.source_range
|
129
129
|
check_no_space(
|
130
|
-
range_with_surrounding_space(
|
130
|
+
range_with_surrounding_space(expr, side: :left).begin_pos,
|
131
131
|
expr.begin_pos - 1,
|
132
132
|
'Extra space before'
|
133
133
|
)
|
@@ -161,7 +161,7 @@ module RuboCop
|
|
161
161
|
end
|
162
162
|
|
163
163
|
def check_operator(type, operator, right_operand)
|
164
|
-
with_space = range_with_surrounding_space(
|
164
|
+
with_space = range_with_surrounding_space(operator)
|
165
165
|
return if with_space.source.start_with?("\n")
|
166
166
|
|
167
167
|
offense(type, operator, with_space, right_operand) do |msg|
|
@@ -65,7 +65,7 @@ module RuboCop
|
|
65
65
|
return if conflict_with_block_delimiters?(node)
|
66
66
|
|
67
67
|
left_brace = node.loc.begin
|
68
|
-
space_plus_brace = range_with_surrounding_space(
|
68
|
+
space_plus_brace = range_with_surrounding_space(left_brace)
|
69
69
|
used_style =
|
70
70
|
space_plus_brace.source.start_with?('{') ? :no_space : :space
|
71
71
|
|
@@ -36,7 +36,7 @@ module RuboCop
|
|
36
36
|
return unless regular_method_call_with_arguments?(node)
|
37
37
|
|
38
38
|
first_arg = node.first_argument.source_range
|
39
|
-
first_arg_with_space = range_with_surrounding_space(
|
39
|
+
first_arg_with_space = range_with_surrounding_space(first_arg, side: :left)
|
40
40
|
space = range_between(first_arg_with_space.begin_pos, first_arg.begin_pos)
|
41
41
|
return if space.length == 1
|
42
42
|
return unless expect_params_after_method_name?(node)
|
@@ -98,6 +98,8 @@ module RuboCop
|
|
98
98
|
check_inside(node, left_brace, right_brace)
|
99
99
|
end
|
100
100
|
|
101
|
+
alias on_numblock on_block
|
102
|
+
|
101
103
|
private
|
102
104
|
|
103
105
|
def check_inside(node, left_brace, right_brace)
|
@@ -126,7 +128,7 @@ module RuboCop
|
|
126
128
|
end
|
127
129
|
|
128
130
|
def braces_with_contents_inside(node, inner)
|
129
|
-
args_delimiter = node.arguments.loc.begin # Can be ( | or nil.
|
131
|
+
args_delimiter = node.arguments.loc.begin if node.block_type? # Can be ( | or nil.
|
130
132
|
|
131
133
|
check_left_brace(inner, node.loc.begin, args_delimiter)
|
132
134
|
check_right_brace(inner, node.loc.begin, node.loc.end, node.single_line?)
|
@@ -185,7 +187,7 @@ module RuboCop
|
|
185
187
|
'Space between { and | detected.')
|
186
188
|
end
|
187
189
|
else
|
188
|
-
brace_with_space = range_with_surrounding_space(
|
190
|
+
brace_with_space = range_with_surrounding_space(left_brace, side: :right)
|
189
191
|
space(brace_with_space.begin_pos + 1, brace_with_space.end_pos,
|
190
192
|
'Space inside { detected.')
|
191
193
|
end
|
@@ -196,7 +198,7 @@ module RuboCop
|
|
196
198
|
end
|
197
199
|
|
198
200
|
def space_inside_right_brace(right_brace)
|
199
|
-
brace_with_space = range_with_surrounding_space(
|
201
|
+
brace_with_space = range_with_surrounding_space(right_brace, side: :left)
|
200
202
|
space(brace_with_space.begin_pos, brace_with_space.end_pos - 1,
|
201
203
|
'Space inside } detected.')
|
202
204
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# Checks for overwriting an exception with an exception result by use `rescue =>`.
|
7
|
+
#
|
8
|
+
# You intended to write as `rescue StandardError`.
|
9
|
+
# However, you have written `rescue => StandardError`.
|
10
|
+
# In that case, the result of `rescue` will overwrite `StandardError`.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
#
|
14
|
+
# # bad
|
15
|
+
# begin
|
16
|
+
# something
|
17
|
+
# rescue => StandardError
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# # good
|
21
|
+
# begin
|
22
|
+
# something
|
23
|
+
# rescue StandardError
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
class ConstantOverwrittenInRescue < Base
|
27
|
+
extend AutoCorrector
|
28
|
+
include RangeHelp
|
29
|
+
|
30
|
+
MSG = '`%<constant>s` is overwritten by `rescue =>`.'
|
31
|
+
|
32
|
+
# @!method overwritten_constant(node)
|
33
|
+
def_node_matcher :overwritten_constant, <<~PATTERN
|
34
|
+
(resbody nil? (casgn nil? $_) nil?)
|
35
|
+
PATTERN
|
36
|
+
|
37
|
+
def self.autocorrect_incompatible_with
|
38
|
+
[Naming::RescuedExceptionsVariableName, Style::RescueStandardError]
|
39
|
+
end
|
40
|
+
|
41
|
+
def on_resbody(node)
|
42
|
+
return unless (constant = overwritten_constant(node))
|
43
|
+
|
44
|
+
add_offense(node.loc.assoc, message: format(MSG, constant: constant)) do |corrector|
|
45
|
+
corrector.remove(range_between(node.loc.keyword.end_pos, node.loc.assoc.end_pos))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -24,7 +24,7 @@ module RuboCop
|
|
24
24
|
class InterpolationCheck < Base
|
25
25
|
extend AutoCorrector
|
26
26
|
|
27
|
-
MSG = 'Interpolation in single quoted string detected. '\
|
27
|
+
MSG = 'Interpolation in single quoted string detected. ' \
|
28
28
|
'Use double quoted strings if you need interpolation.'
|
29
29
|
|
30
30
|
def on_str(node)
|
@@ -7,6 +7,9 @@ module RuboCop
|
|
7
7
|
# operands in and/or expressions serving as the conditions of
|
8
8
|
# if/while/until/case-when/case-in.
|
9
9
|
#
|
10
|
+
# NOTE: Literals in `case-in` condition where the match variable is used in
|
11
|
+
# `in` are accepted as a pattern matching.
|
12
|
+
#
|
10
13
|
# @example
|
11
14
|
#
|
12
15
|
# # bad
|
@@ -69,6 +72,8 @@ module RuboCop
|
|
69
72
|
|
70
73
|
def on_case_match(case_match_node)
|
71
74
|
if case_match_node.condition
|
75
|
+
return if case_match_node.descendants.any?(&:match_var_type?)
|
76
|
+
|
72
77
|
check_case(case_match_node)
|
73
78
|
else
|
74
79
|
case_match_node.each_in_pattern do |in_pattern_node|
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# Checks for non-atomic file operation.
|
7
|
+
# And then replace it with a nearly equivalent and atomic method.
|
8
|
+
#
|
9
|
+
# These can cause problems that are difficult to reproduce,
|
10
|
+
# especially in cases of frequent file operations in parallel,
|
11
|
+
# such as test runs with parallel_rspec.
|
12
|
+
#
|
13
|
+
# For examples: creating a directory if there is none, has the following problems
|
14
|
+
#
|
15
|
+
# An exception occurs when the directory didn't exist at the time of `exist?`,
|
16
|
+
# but someone else created it before `mkdir` was executed.
|
17
|
+
#
|
18
|
+
# Subsequent processes are executed without the directory that should be there
|
19
|
+
# when the directory existed at the time of `exist?`,
|
20
|
+
# but someone else deleted it shortly afterwards.
|
21
|
+
#
|
22
|
+
# @safety
|
23
|
+
# This cop is unsafe, because autocorrection change to atomic processing.
|
24
|
+
# The atomic processing of the replacement destination is not guaranteed
|
25
|
+
# to be strictly equivalent to that before the replacement.
|
26
|
+
#
|
27
|
+
# @example
|
28
|
+
# # bad
|
29
|
+
# unless FileTest.exist?(path)
|
30
|
+
# FileUtils.makedirs(path)
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# if FileTest.exist?(path)
|
34
|
+
# FileUtils.remove(path)
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# # good
|
38
|
+
# FileUtils.mkdir_p(path)
|
39
|
+
#
|
40
|
+
# FileUtils.rm_rf(path)
|
41
|
+
#
|
42
|
+
class NonAtomicFileOperation < Base
|
43
|
+
extend AutoCorrector
|
44
|
+
include Alignment
|
45
|
+
include RangeHelp
|
46
|
+
|
47
|
+
MSG = 'Remove unnecessary existence checks `%<receiver>s.%<method_name>s`.'
|
48
|
+
MAKE_METHODS = %i[makedirs mkdir mkdir_p mkpath].freeze
|
49
|
+
REMOVE_METHODS = %i[remove remove_dir remove_entry remove_entry_secure delete unlink
|
50
|
+
remove_file rm rm_f rm_r rm_rf rmdir rmtree safe_unlink].freeze
|
51
|
+
RESTRICT_ON_SEND = (MAKE_METHODS + REMOVE_METHODS).freeze
|
52
|
+
|
53
|
+
# @!method send_exist_node(node)
|
54
|
+
def_node_search :send_exist_node, <<-PATTERN
|
55
|
+
$(send (const nil? {:FileTest :File :Dir :Shell}) {:exist? :exists?} ...)
|
56
|
+
PATTERN
|
57
|
+
|
58
|
+
# @!method receiver_and_method_name(node)
|
59
|
+
def_node_matcher :receiver_and_method_name, <<-PATTERN
|
60
|
+
(send (const nil? $_) $_ ...)
|
61
|
+
PATTERN
|
62
|
+
|
63
|
+
# @!method force?(node)
|
64
|
+
def_node_search :force?, <<~PATTERN
|
65
|
+
(pair (sym :force) (:true))
|
66
|
+
PATTERN
|
67
|
+
|
68
|
+
# @!method explicit_not_force?(node)
|
69
|
+
def_node_search :explicit_not_force?, <<~PATTERN
|
70
|
+
(pair (sym :force) (:false))
|
71
|
+
PATTERN
|
72
|
+
|
73
|
+
def on_send(node)
|
74
|
+
return unless node.parent&.if_type?
|
75
|
+
return if node.parent.else_branch
|
76
|
+
return if explicit_not_force?(node)
|
77
|
+
return unless (exist_node = send_exist_node(node.parent).first)
|
78
|
+
return unless exist_node.first_argument == node.first_argument
|
79
|
+
|
80
|
+
offense(node, exist_node)
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def offense(node, exist_node)
|
86
|
+
range = range_between(node.parent.loc.keyword.begin_pos,
|
87
|
+
exist_node.loc.expression.end_pos)
|
88
|
+
|
89
|
+
add_offense(range, message: message(exist_node)) do |corrector|
|
90
|
+
autocorrect(corrector, node, range)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def message(node)
|
95
|
+
receiver, method_name = receiver_and_method_name(node)
|
96
|
+
format(MSG, receiver: receiver, method_name: method_name)
|
97
|
+
end
|
98
|
+
|
99
|
+
def autocorrect(corrector, node, range)
|
100
|
+
corrector.remove(range)
|
101
|
+
corrector.replace(node.child_nodes.first.loc.name, 'FileUtils')
|
102
|
+
corrector.replace(node.loc.selector, replacement_method(node))
|
103
|
+
corrector.remove(node.parent.loc.end) if node.parent.multiline?
|
104
|
+
end
|
105
|
+
|
106
|
+
def replacement_method(node)
|
107
|
+
return node.method_name if force_option?(node)
|
108
|
+
|
109
|
+
if MAKE_METHODS.include?(node.method_name)
|
110
|
+
'mkdir_p'
|
111
|
+
elsif REMOVE_METHODS.include?(node.method_name)
|
112
|
+
'rm_rf'
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def force_option?(node)
|
117
|
+
node.arguments.any? { |arg| force?(arg) }
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -64,9 +64,9 @@ module RuboCop
|
|
64
64
|
to_c: "#{Complex.name}(%<number_object>s)",
|
65
65
|
to_r: "#{Rational.name}(%<number_object>s)"
|
66
66
|
}.freeze
|
67
|
-
MSG = 'Replace unsafe number conversion with number '\
|
68
|
-
'class parsing, instead of using '\
|
69
|
-
'`%<current>s`, use stricter '\
|
67
|
+
MSG = 'Replace unsafe number conversion with number ' \
|
68
|
+
'class parsing, instead of using ' \
|
69
|
+
'`%<current>s`, use stricter ' \
|
70
70
|
'`%<corrected_method>s`.'
|
71
71
|
CONVERSION_METHODS = %i[Integer Float Complex Rational to_i to_f to_c to_r].freeze
|
72
72
|
METHODS = CONVERSION_METHOD_CLASS_MAPPING.keys.map(&:inspect).join(' ')
|
@@ -55,7 +55,7 @@ module RuboCop
|
|
55
55
|
|
56
56
|
def chained_calls?(node)
|
57
57
|
first_argument = node.first_argument
|
58
|
-
first_argument.
|
58
|
+
first_argument.call_type? && (node.children.last&.children&.count || 0) > 1
|
59
59
|
end
|
60
60
|
|
61
61
|
def ternary_expression?(node)
|