rubocop 1.87.0 → 1.88.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/config/default.yml +78 -72
- data/config/obsoletion.yml +21 -1
- data/lib/rubocop/cli/command/auto_generate_config.rb +6 -0
- data/lib/rubocop/cop/base.rb +17 -2
- data/lib/rubocop/cop/bundler/gem_comment.rb +5 -3
- data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +1 -1
- data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +7 -1
- data/lib/rubocop/cop/correctors/ordered_gem_corrector.rb +8 -1
- data/lib/rubocop/cop/gemspec/development_dependencies.rb +1 -1
- data/lib/rubocop/cop/gemspec/require_mfa.rb +4 -1
- data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +5 -3
- data/lib/rubocop/cop/layout/block_alignment.rb +58 -4
- data/lib/rubocop/cop/layout/class_structure.rb +7 -3
- data/lib/rubocop/cop/layout/condition_position.rb +13 -3
- data/lib/rubocop/cop/layout/empty_comment.rb +8 -10
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +14 -1
- data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +13 -14
- data/lib/rubocop/cop/layout/indentation_width.rb +28 -0
- data/lib/rubocop/cop/layout/space_around_operators.rb +6 -2
- data/lib/rubocop/cop/lint/ambiguous_assignment.rb +1 -11
- data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +1 -10
- data/lib/rubocop/cop/lint/assignment_in_condition.rb +13 -1
- data/lib/rubocop/cop/lint/circular_argument_reference.rb +1 -3
- data/lib/rubocop/cop/lint/debugger.rb +0 -1
- data/lib/rubocop/cop/lint/deprecated_constants.rb +1 -7
- data/lib/rubocop/cop/lint/empty_block.rb +3 -3
- data/lib/rubocop/cop/lint/ensure_return.rb +19 -1
- data/lib/rubocop/cop/lint/erb_new_arguments.rb +3 -1
- data/lib/rubocop/cop/lint/float_comparison.rb +1 -0
- data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +5 -1
- data/lib/rubocop/cop/lint/interpolation_check.rb +18 -3
- data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +1 -1
- data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +11 -1
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +8 -11
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +4 -4
- data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +16 -0
- data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +1 -1
- data/lib/rubocop/cop/lint/number_conversion.rb +13 -4
- data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +3 -0
- data/lib/rubocop/cop/lint/ordered_magic_comments.rb +7 -7
- data/lib/rubocop/cop/lint/raise_exception.rb +1 -1
- data/lib/rubocop/cop/lint/rand_one.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +4 -1
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +4 -1
- data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +15 -4
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +14 -7
- data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +4 -0
- data/lib/rubocop/cop/lint/redundant_type_conversion.rb +7 -0
- data/lib/rubocop/cop/lint/redundant_with_index.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_with_object.rb +5 -0
- data/lib/rubocop/cop/lint/refinement_import_methods.rb +8 -1
- data/lib/rubocop/cop/lint/regexp_as_condition.rb +9 -1
- data/lib/rubocop/cop/lint/require_parentheses.rb +13 -4
- data/lib/rubocop/cop/lint/require_range_parentheses.rb +2 -1
- data/lib/rubocop/cop/lint/require_relative_self_path.rb +5 -5
- data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +1 -0
- data/lib/rubocop/cop/lint/safe_navigation_with_empty.rb +1 -1
- data/lib/rubocop/cop/lint/script_permission.rb +5 -1
- data/lib/rubocop/cop/lint/self_assignment.rb +24 -1
- data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +1 -1
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +14 -0
- data/lib/rubocop/cop/lint/shared_mutable_default.rb +3 -1
- data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +12 -0
- data/lib/rubocop/cop/lint/symbol_conversion.rb +21 -4
- data/lib/rubocop/cop/lint/to_enum_arguments.rb +35 -2
- data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +1 -1
- data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +4 -1
- data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +35 -9
- data/lib/rubocop/cop/lint/useless_assignment.rb +10 -5
- data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +7 -3
- data/lib/rubocop/cop/lint/useless_setter_call.rb +4 -1
- data/lib/rubocop/cop/lint/useless_times.rb +22 -1
- data/lib/rubocop/cop/metrics/collection_literal_length.rb +1 -1
- data/lib/rubocop/cop/metrics/method_length.rb +1 -1
- data/lib/rubocop/cop/metrics/perceived_complexity.rb +38 -7
- data/lib/rubocop/cop/mixin/hash_subset.rb +8 -0
- data/lib/rubocop/cop/mixin/hash_transform_method.rb +4 -0
- data/lib/rubocop/cop/naming/file_name.rb +4 -3
- data/lib/rubocop/cop/naming/inclusive_language.rb +8 -2
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +9 -0
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +9 -3
- data/lib/rubocop/cop/security/io_methods.rb +1 -1
- data/lib/rubocop/cop/security/marshal_load.rb +1 -1
- data/lib/rubocop/cop/style/accessor_grouping.rb +11 -1
- data/lib/rubocop/cop/style/alias.rb +1 -1
- data/lib/rubocop/cop/style/and_or.rb +1 -1
- data/lib/rubocop/cop/style/array_first_last.rb +12 -1
- data/lib/rubocop/cop/style/array_intersect.rb +4 -0
- data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +3 -0
- data/lib/rubocop/cop/style/block_delimiters.rb +16 -2
- data/lib/rubocop/cop/style/case_equality.rb +14 -2
- data/lib/rubocop/cop/style/class_equality_comparison.rb +21 -13
- data/lib/rubocop/cop/style/class_methods_definitions.rb +11 -5
- data/lib/rubocop/cop/style/colon_method_call.rb +13 -6
- data/lib/rubocop/cop/style/combinable_loops.rb +5 -0
- data/lib/rubocop/cop/style/comparable_clamp.rb +12 -1
- data/lib/rubocop/cop/style/concat_array_literals.rb +5 -1
- data/lib/rubocop/cop/style/conditional_assignment.rb +6 -1
- data/lib/rubocop/cop/style/constant_visibility.rb +4 -1
- data/lib/rubocop/cop/style/data_inheritance.rb +4 -0
- data/lib/rubocop/cop/style/date_time.rb +2 -2
- data/lib/rubocop/cop/style/dig_chain.rb +5 -0
- data/lib/rubocop/cop/style/dir_empty.rb +4 -0
- data/lib/rubocop/cop/style/empty_case_condition.rb +12 -2
- data/lib/rubocop/cop/style/empty_class_definition.rb +8 -1
- data/lib/rubocop/cop/style/empty_heredoc.rb +4 -0
- data/lib/rubocop/cop/style/empty_literal.rb +7 -2
- data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +30 -20
- data/lib/rubocop/cop/style/env_home.rb +4 -0
- data/lib/rubocop/cop/style/even_odd.rb +11 -1
- data/lib/rubocop/cop/style/exact_regexp_match.rb +8 -1
- data/lib/rubocop/cop/style/fetch_env_var.rb +1 -1
- data/lib/rubocop/cop/style/file_null.rb +4 -2
- data/lib/rubocop/cop/style/file_write.rb +17 -14
- data/lib/rubocop/cop/style/format_string.rb +13 -1
- data/lib/rubocop/cop/style/hash_slice.rb +16 -0
- data/lib/rubocop/cop/style/hash_syntax.rb +2 -0
- data/lib/rubocop/cop/style/if_unless_modifier.rb +1 -1
- data/lib/rubocop/cop/style/if_with_semicolon.rb +9 -1
- data/lib/rubocop/cop/style/inline_comment.rb +1 -1
- data/lib/rubocop/cop/style/keyword_arguments_merging.rb +4 -0
- data/lib/rubocop/cop/style/keyword_parameters_order.rb +7 -3
- data/lib/rubocop/cop/style/lambda.rb +7 -1
- data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +11 -0
- data/lib/rubocop/cop/style/map_into_array.rb +1 -1
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +6 -2
- data/lib/rubocop/cop/style/method_def_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/min_max_comparison.rb +3 -0
- data/lib/rubocop/cop/style/multiline_if_then.rb +1 -1
- data/lib/rubocop/cop/style/multiline_memoization.rb +7 -1
- data/lib/rubocop/cop/style/multiline_method_signature.rb +11 -4
- data/lib/rubocop/cop/style/mutable_constant.rb +105 -11
- data/lib/rubocop/cop/style/nil_lambda.rb +8 -0
- data/lib/rubocop/cop/style/numeric_predicate.rb +1 -1
- data/lib/rubocop/cop/style/open_struct_use.rb +1 -1
- data/lib/rubocop/cop/style/option_hash.rb +1 -1
- data/lib/rubocop/cop/style/optional_arguments.rb +1 -0
- data/lib/rubocop/cop/style/parallel_assignment.rb +19 -3
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +2 -0
- data/lib/rubocop/cop/style/perl_backrefs.rb +5 -3
- data/lib/rubocop/cop/style/redundant_exception.rb +6 -0
- data/lib/rubocop/cop/style/redundant_filter_chain.rb +1 -1
- data/lib/rubocop/cop/style/redundant_format.rb +29 -0
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +11 -3
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -4
- data/lib/rubocop/cop/style/redundant_self.rb +9 -0
- data/lib/rubocop/cop/style/redundant_struct_keyword_init.rb +23 -4
- data/lib/rubocop/cop/style/semicolon.rb +20 -5
- data/lib/rubocop/cop/style/single_line_do_end_block.rb +17 -4
- data/lib/rubocop/cop/style/string_hash_keys.rb +1 -0
- data/lib/rubocop/cop/style/ternary_parentheses.rb +11 -0
- data/lib/rubocop/cop/style/trailing_underscore_variable.rb +7 -8
- data/lib/rubocop/cop/style/while_until_do.rb +7 -0
- data/lib/rubocop/cop/style/word_array.rb +1 -0
- data/lib/rubocop/cop/style/zero_length_predicate.rb +6 -3
- data/lib/rubocop/formatter/disabled_config_formatter.rb +14 -7
- data/lib/rubocop/runner.rb +5 -3
- data/lib/rubocop/server/core.rb +6 -0
- data/lib/rubocop/version.rb +1 -1
- metadata +3 -3
|
@@ -128,8 +128,8 @@ module RuboCop
|
|
|
128
128
|
end
|
|
129
129
|
|
|
130
130
|
def ignored_gem?(node)
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
allowed_gems = Array(cop_config['AllowedGems'])
|
|
132
|
+
allowed_gems.include?(node.first_argument.value)
|
|
133
133
|
end
|
|
134
134
|
|
|
135
135
|
def checked_options_present?(node)
|
|
@@ -163,7 +163,9 @@ module RuboCop
|
|
|
163
163
|
def gem_options(node)
|
|
164
164
|
return [] unless node.last_argument&.hash_type?
|
|
165
165
|
|
|
166
|
-
|
|
166
|
+
# Only literal keys carry an option name to check; a non-literal key
|
|
167
|
+
# (e.g. a variable or method call) has no `value` and must be skipped.
|
|
168
|
+
node.last_argument.keys.filter_map { |key| key.value if key.type?(:sym, :str) }
|
|
167
169
|
end
|
|
168
170
|
end
|
|
169
171
|
end
|
|
@@ -27,7 +27,7 @@ module RuboCop
|
|
|
27
27
|
if block_node.arguments?
|
|
28
28
|
format(CORRECTION_WITH_ARGUMENTS,
|
|
29
29
|
collection: collection_node.source,
|
|
30
|
-
variables: argument_node.children.
|
|
30
|
+
variables: argument_node.children.map(&:source).join(', '))
|
|
31
31
|
else
|
|
32
32
|
format(CORRECTION_WITHOUT_ARGUMENTS, enumerable: collection_node.source)
|
|
33
33
|
end
|
|
@@ -86,7 +86,13 @@ module RuboCop
|
|
|
86
86
|
end
|
|
87
87
|
|
|
88
88
|
def lambda_arg_string
|
|
89
|
-
|
|
89
|
+
# Block-local (shadow) arguments are separated from regular arguments by a
|
|
90
|
+
# `;`; joining everything with `,` would turn them into extra parameters
|
|
91
|
+
# and change the lambda's arity.
|
|
92
|
+
regular, shadow = arguments.children.partition { |arg| !arg.shadowarg_type? }
|
|
93
|
+
arg_string = regular.map(&:source).join(', ')
|
|
94
|
+
arg_string += "; #{shadow.map(&:source).join(', ')}" unless shadow.empty?
|
|
95
|
+
arg_string
|
|
90
96
|
end
|
|
91
97
|
|
|
92
98
|
def needs_separating_space?
|
|
@@ -18,7 +18,14 @@ module RuboCop
|
|
|
18
18
|
current_range = declaration_with_comment(node)
|
|
19
19
|
previous_range = declaration_with_comment(previous_declaration)
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
lambda do |corrector|
|
|
22
|
+
if current_range.source.end_with?("\n")
|
|
23
|
+
corrector.swap(current_range, previous_range)
|
|
24
|
+
else
|
|
25
|
+
corrector.insert_before(previous_range, "#{current_range.source}\n")
|
|
26
|
+
corrector.remove(current_range)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
22
29
|
end
|
|
23
30
|
|
|
24
31
|
private
|
|
@@ -143,7 +143,10 @@ module RuboCop
|
|
|
143
143
|
#{block_var}.metadata['rubygems_mfa_required'] = 'true'
|
|
144
144
|
RUBY
|
|
145
145
|
|
|
146
|
-
|
|
146
|
+
# Scope the search to the current spec block. Searching the whole file
|
|
147
|
+
# would, for a second `Gem::Specification.new` block, insert the directive
|
|
148
|
+
# into the first block, leaving this block uncorrected and looping forever.
|
|
149
|
+
if (last_assignment = metadata_assignment(node).to_a.last)
|
|
147
150
|
corrector.insert_after(last_assignment, "\n#{require_mfa_directive}")
|
|
148
151
|
else
|
|
149
152
|
corrector.insert_before(node.loc.end, "#{require_mfa_directive}\n")
|
|
@@ -46,8 +46,7 @@ module RuboCop
|
|
|
46
46
|
|
|
47
47
|
def on_block(node)
|
|
48
48
|
return unless let_rubocop_config_new?(node)
|
|
49
|
-
|
|
50
|
-
describe = find_describe_method_node(node)
|
|
49
|
+
return unless (describe = find_describe_method_node(node))
|
|
51
50
|
|
|
52
51
|
unless (exist_config = describe.last_argument.source == ':config')
|
|
53
52
|
additional_message = ' and specify `:config` in `describe`'
|
|
@@ -65,7 +64,10 @@ module RuboCop
|
|
|
65
64
|
private
|
|
66
65
|
|
|
67
66
|
def find_describe_method_node(block_node)
|
|
68
|
-
block_node.ancestors.find
|
|
67
|
+
describe = block_node.ancestors.find do |ancestor|
|
|
68
|
+
ancestor.block_type? && ancestor.method?(:describe)
|
|
69
|
+
end
|
|
70
|
+
describe&.send_node
|
|
69
71
|
end
|
|
70
72
|
end
|
|
71
73
|
end
|
|
@@ -18,6 +18,10 @@ module RuboCop
|
|
|
18
18
|
# `either` (which is the default) : the `end` is allowed to be in either
|
|
19
19
|
# location. The autocorrect will default to `start_of_line`.
|
|
20
20
|
#
|
|
21
|
+
# When the `do` or `{` appears on a continuation line of multiline
|
|
22
|
+
# method arguments, the start of the line where the method is called
|
|
23
|
+
# is used as the alignment target instead of that continuation line.
|
|
24
|
+
#
|
|
21
25
|
# @example EnforcedStyleAlignWith: either (default)
|
|
22
26
|
# # bad
|
|
23
27
|
#
|
|
@@ -116,6 +120,7 @@ module RuboCop
|
|
|
116
120
|
end_loc = block_node.loc.end
|
|
117
121
|
return unless begins_its_line?(end_loc)
|
|
118
122
|
|
|
123
|
+
start_node = start_for_line_node(block_node) if style == :start_of_line
|
|
119
124
|
start_loc = start_node.source_range
|
|
120
125
|
return unless start_loc.column != end_loc.column || style == :start_of_block
|
|
121
126
|
|
|
@@ -196,23 +201,34 @@ module RuboCop
|
|
|
196
201
|
|
|
197
202
|
def compute_do_source_line_column(node, end_loc)
|
|
198
203
|
do_loc = node.loc.begin # Actually it's either do or {.
|
|
204
|
+
anchor_loc = do_line_anchor_loc(node, do_loc)
|
|
199
205
|
|
|
200
206
|
# We've found that "end" is not aligned with the start node (which
|
|
201
207
|
# can be a block, a variable assignment, etc). But we also allow
|
|
202
208
|
# the "end" to be aligned with the start of the line where the "do"
|
|
203
209
|
# is, which is a style some people use in multi-line chains of
|
|
204
210
|
# blocks.
|
|
205
|
-
match = /\S.*/.match(
|
|
211
|
+
match = /\S.*/.match(anchor_loc.source_line)
|
|
206
212
|
indentation_of_do_line = match.begin(0)
|
|
207
|
-
|
|
213
|
+
permitted_columns = permitted_do_line_columns(do_loc, indentation_of_do_line)
|
|
214
|
+
return if permitted_columns.include?(end_loc.column) && style != :start_of_line
|
|
208
215
|
|
|
209
216
|
{
|
|
210
217
|
source: match[0],
|
|
211
|
-
line:
|
|
218
|
+
line: anchor_loc.line,
|
|
212
219
|
column: indentation_of_do_line
|
|
213
220
|
}
|
|
214
221
|
end
|
|
215
222
|
|
|
223
|
+
# `end` aligned with an argument continuation line that holds the `do`
|
|
224
|
+
# was accepted before the anchor moved to the method dispatch line;
|
|
225
|
+
# keep accepting it so that such code does not become an offense.
|
|
226
|
+
def permitted_do_line_columns(do_loc, indentation_of_do_line)
|
|
227
|
+
columns = [indentation_of_do_line]
|
|
228
|
+
columns << (do_loc.source_line =~ /\S/) if style == :either
|
|
229
|
+
columns
|
|
230
|
+
end
|
|
231
|
+
|
|
216
232
|
def loc_to_source_line_column(loc)
|
|
217
233
|
{
|
|
218
234
|
source: loc.source.lines.to_a.first.chomp,
|
|
@@ -239,7 +255,7 @@ module RuboCop
|
|
|
239
255
|
def compute_start_col(ancestor_node, node)
|
|
240
256
|
if style == :start_of_block
|
|
241
257
|
do_loc = node.loc.begin
|
|
242
|
-
return do_loc.source_line =~ /\S/
|
|
258
|
+
return do_line_anchor_loc(node, do_loc).source_line =~ /\S/
|
|
243
259
|
end
|
|
244
260
|
(ancestor_node || node).source_range.column
|
|
245
261
|
end
|
|
@@ -253,6 +269,44 @@ module RuboCop
|
|
|
253
269
|
|
|
254
270
|
corrector.remove(range)
|
|
255
271
|
end
|
|
272
|
+
|
|
273
|
+
# When the `do` or `{` is on a continuation line of multiline method
|
|
274
|
+
# arguments, the indentation of that line is not a meaningful
|
|
275
|
+
# alignment target; anchor on the method dispatch position instead.
|
|
276
|
+
def do_line_anchor_loc(node, do_loc)
|
|
277
|
+
if do_line_begins_inside_argument?(node, do_loc)
|
|
278
|
+
node.send_node.selector || node.send_node.source_range
|
|
279
|
+
else
|
|
280
|
+
do_loc
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
# rubocop:disable Metrics/AbcSize
|
|
285
|
+
def do_line_begins_inside_argument?(node, do_loc)
|
|
286
|
+
line_begin_pos = do_loc.begin_pos - do_loc.column
|
|
287
|
+
first_char_pos = line_begin_pos + (do_loc.source_line =~ /\S/)
|
|
288
|
+
return false unless inside_parentheses?(node, first_char_pos)
|
|
289
|
+
|
|
290
|
+
(node.send_node.arguments + node.arguments).any? do |argument|
|
|
291
|
+
argument.source_range.begin_pos <= first_char_pos &&
|
|
292
|
+
first_char_pos < argument.source_range.end_pos
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
# rubocop:enable Metrics/AbcSize
|
|
296
|
+
|
|
297
|
+
# The continuation line indentation is only an unmeaningful alignment target when
|
|
298
|
+
# it is dictated by an opening delimiter, i.e. the line begins inside `(` or `[`.
|
|
299
|
+
# For a bare argument list without parentheses the indentation is the author's
|
|
300
|
+
# deliberate alignment, so the anchor must not move to the method dispatch line.
|
|
301
|
+
def inside_parentheses?(node, pos)
|
|
302
|
+
preceding_tokens = processed_source.tokens.select do |token|
|
|
303
|
+
token.begin_pos >= node.source_range.begin_pos && token.begin_pos < pos
|
|
304
|
+
end
|
|
305
|
+
opens = preceding_tokens.count { |token| token.left_parens? || token.left_bracket? }
|
|
306
|
+
closes = preceding_tokens.count { |token| token.right_parens? || token.right_bracket? }
|
|
307
|
+
|
|
308
|
+
opens > closes
|
|
309
|
+
end
|
|
256
310
|
end
|
|
257
311
|
end
|
|
258
312
|
end
|
|
@@ -278,10 +278,14 @@ module RuboCop
|
|
|
278
278
|
|
|
279
279
|
return [] unless class_def
|
|
280
280
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
281
|
+
# Only a multi-statement body (`begin`/`kwbegin`) wraps several elements; any
|
|
282
|
+
# single statement (`def`, `send`, `csend`, `if`, ...) is itself the sole element.
|
|
283
|
+
# Exploding such a node into its children would yield non-node values (e.g. a
|
|
284
|
+
# method-name `Symbol` from a `csend`) and crash later checks.
|
|
285
|
+
if class_def.type?(:begin, :kwbegin)
|
|
284
286
|
class_def.children.compact
|
|
287
|
+
else
|
|
288
|
+
[class_def]
|
|
285
289
|
end
|
|
286
290
|
end
|
|
287
291
|
|
|
@@ -44,16 +44,26 @@ module RuboCop
|
|
|
44
44
|
message = message(condition)
|
|
45
45
|
|
|
46
46
|
add_offense(condition, message: message) do |corrector|
|
|
47
|
-
range = range_by_whole_lines(condition.source_range, include_final_newline: true)
|
|
48
|
-
|
|
49
47
|
corrector.insert_after(condition.parent.loc.keyword, " #{condition.source}")
|
|
50
|
-
corrector.remove(
|
|
48
|
+
corrector.remove(removal_range(node, condition))
|
|
51
49
|
end
|
|
52
50
|
end
|
|
53
51
|
|
|
54
52
|
def message(condition)
|
|
55
53
|
format(MSG, keyword: condition.parent.keyword)
|
|
56
54
|
end
|
|
55
|
+
|
|
56
|
+
# When a body statement shares the condition's line (e.g. `while\n cond; body\nend`),
|
|
57
|
+
# removing the whole line would delete the body too. In that case only remove the
|
|
58
|
+
# condition and its trailing separator, preserving the body statement.
|
|
59
|
+
def removal_range(node, condition)
|
|
60
|
+
body = node.body
|
|
61
|
+
if body && body.source_range.line == condition.source_range.last_line
|
|
62
|
+
range_between(condition.source_range.begin_pos, body.source_range.begin_pos)
|
|
63
|
+
else
|
|
64
|
+
range_by_whole_lines(condition.source_range, include_final_newline: true)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
57
67
|
end
|
|
58
68
|
end
|
|
59
69
|
end
|
|
@@ -95,8 +95,7 @@ module RuboCop
|
|
|
95
95
|
end
|
|
96
96
|
|
|
97
97
|
def autocorrect(corrector, node)
|
|
98
|
-
|
|
99
|
-
range = if previous_token && same_line?(node, previous_token)
|
|
98
|
+
range = if inline_comment?(node)
|
|
100
99
|
range_with_surrounding_space(node.source_range, newlines: false)
|
|
101
100
|
else
|
|
102
101
|
range_by_whole_lines(node.source_range, include_final_newline: true)
|
|
@@ -138,14 +137,13 @@ module RuboCop
|
|
|
138
137
|
cop_config['AllowMarginComment']
|
|
139
138
|
end
|
|
140
139
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
def
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
index.zero? ? nil : processed_source.tokens[index - 1]
|
|
140
|
+
# A comment is inline when code precedes it on the same line. Detecting this
|
|
141
|
+
# from the source (rather than the token stream) is required because, for a
|
|
142
|
+
# comment trailing a heredoc opener, the preceding token is the heredoc end on
|
|
143
|
+
# a later line, which would wrongly trigger whole-line removal of the opener.
|
|
144
|
+
def inline_comment?(comment)
|
|
145
|
+
preceding_source = processed_source.lines[comment.loc.line - 1][0...comment.loc.column]
|
|
146
|
+
!preceding_source.strip.empty?
|
|
149
147
|
end
|
|
150
148
|
end
|
|
151
149
|
end
|
|
@@ -274,7 +274,20 @@ module RuboCop
|
|
|
274
274
|
end
|
|
275
275
|
|
|
276
276
|
def end_loc(node)
|
|
277
|
-
node.source_range.end
|
|
277
|
+
end_location = node.source_range.end
|
|
278
|
+
trailing_heredoc_end(node, end_location) || end_location
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# For an endless method whose body is a heredoc (e.g. `def a = <<~TEXT`), the
|
|
282
|
+
# node's source range ends at the heredoc opening line, before the heredoc body.
|
|
283
|
+
# Use the heredoc's closing delimiter so the def's real end is located after the
|
|
284
|
+
# heredoc and blank-line insertion does not land inside the heredoc body.
|
|
285
|
+
def trailing_heredoc_end(node, end_location)
|
|
286
|
+
heredocs = node.each_descendant(:any_str).select(&:heredoc?)
|
|
287
|
+
return if heredocs.empty?
|
|
288
|
+
|
|
289
|
+
heredoc_end = heredocs.map { |heredoc| heredoc.loc.heredoc_end }.max_by(&:end_pos)
|
|
290
|
+
heredoc_end if heredoc_end.end_pos > end_location.end_pos
|
|
278
291
|
end
|
|
279
292
|
|
|
280
293
|
def autocorrect_remove_lines(corrector, newline_pos, count)
|
|
@@ -28,10 +28,10 @@ module RuboCop
|
|
|
28
28
|
# # bad
|
|
29
29
|
# hash = {
|
|
30
30
|
# key: :value
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
31
|
+
# }
|
|
32
|
+
# in_a_method_call({
|
|
33
|
+
# foo: :bar
|
|
34
|
+
# })
|
|
35
35
|
# takes_multi_pairs_hash(x: {
|
|
36
36
|
# a: 1,
|
|
37
37
|
# b: 2
|
|
@@ -42,13 +42,12 @@ module RuboCop
|
|
|
42
42
|
# })
|
|
43
43
|
#
|
|
44
44
|
# # good
|
|
45
|
-
# special_inside_parentheses
|
|
46
45
|
# hash = {
|
|
47
46
|
# key: :value
|
|
48
47
|
# }
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
#
|
|
48
|
+
# in_a_method_call({
|
|
49
|
+
# foo: :bar
|
|
50
|
+
# })
|
|
52
51
|
# takes_multi_pairs_hash(x: {
|
|
53
52
|
# a: 1,
|
|
54
53
|
# b: 2
|
|
@@ -67,17 +66,17 @@ module RuboCop
|
|
|
67
66
|
# # bad
|
|
68
67
|
# hash = {
|
|
69
68
|
# key: :value
|
|
70
|
-
#
|
|
71
|
-
#
|
|
72
|
-
#
|
|
73
|
-
#
|
|
69
|
+
# }
|
|
70
|
+
# in_a_method_call({
|
|
71
|
+
# foo: :bar
|
|
72
|
+
# })
|
|
74
73
|
#
|
|
75
74
|
# # good
|
|
76
75
|
# hash = {
|
|
77
76
|
# key: :value
|
|
78
77
|
# }
|
|
79
|
-
#
|
|
80
|
-
#
|
|
78
|
+
# in_a_method_call({
|
|
79
|
+
# foo: :bar
|
|
81
80
|
# })
|
|
82
81
|
#
|
|
83
82
|
#
|
|
@@ -25,6 +25,17 @@ module RuboCop
|
|
|
25
25
|
# end
|
|
26
26
|
# end
|
|
27
27
|
#
|
|
28
|
+
# @example
|
|
29
|
+
# # bad
|
|
30
|
+
# value = (
|
|
31
|
+
# foo - bar
|
|
32
|
+
# )
|
|
33
|
+
#
|
|
34
|
+
# # good
|
|
35
|
+
# value = (
|
|
36
|
+
# foo - bar
|
|
37
|
+
# )
|
|
38
|
+
#
|
|
28
39
|
# @example AllowedPatterns: ['^\s*module']
|
|
29
40
|
# # bad
|
|
30
41
|
# module A
|
|
@@ -95,6 +106,16 @@ module RuboCop
|
|
|
95
106
|
check_indentation(node.loc.end, node.children.first)
|
|
96
107
|
end
|
|
97
108
|
|
|
109
|
+
def on_begin(node)
|
|
110
|
+
# Only a parenthesized grouping expression (e.g. `(\n foo\n)`) has
|
|
111
|
+
# explicit delimiters. Indent the body one step from the line
|
|
112
|
+
# the opening parenthesis is on, but only when the closing parenthesis is
|
|
113
|
+
# first on its line (a body on the opening line is skipped downstream).
|
|
114
|
+
return unless parentheses?(node) && begins_its_line?(node.loc.end)
|
|
115
|
+
|
|
116
|
+
check_indentation(opening_line_start(node.loc.begin), node.children.first)
|
|
117
|
+
end
|
|
118
|
+
|
|
98
119
|
def on_block(node)
|
|
99
120
|
end_loc = node.loc.end
|
|
100
121
|
|
|
@@ -188,6 +209,13 @@ module RuboCop
|
|
|
188
209
|
AlignmentCorrector.correct(corrector, processed_source, node, @column_delta)
|
|
189
210
|
end
|
|
190
211
|
|
|
212
|
+
# Returns a range at the first non-space column of the line the opening parenthesis is on,
|
|
213
|
+
# used as the base to indent the body from.
|
|
214
|
+
def opening_line_start(begin_loc)
|
|
215
|
+
column = begin_loc.source_line =~ /\S/
|
|
216
|
+
source_range(processed_source.buffer, begin_loc.line, column)
|
|
217
|
+
end
|
|
218
|
+
|
|
191
219
|
def check_members(base, members)
|
|
192
220
|
check_indentation(base, select_check_member(members.first))
|
|
193
221
|
|
|
@@ -204,10 +204,14 @@ module RuboCop
|
|
|
204
204
|
|
|
205
205
|
def autocorrect(corrector, range, right_operand)
|
|
206
206
|
range_source = range.source
|
|
207
|
+
# Match the operator exactly, not by substring, so compound assignments
|
|
208
|
+
# like `**=` and `/=` are not mistaken for `**` and `/` (which would drop
|
|
209
|
+
# the `=` and silently change the program's behavior).
|
|
210
|
+
operator = range_source.strip
|
|
207
211
|
|
|
208
|
-
if
|
|
212
|
+
if operator == '**' && !space_around_exponent_operator?
|
|
209
213
|
corrector.replace(range, '**')
|
|
210
|
-
elsif
|
|
214
|
+
elsif operator == '/' && !space_around_slash_operator?(right_operand)
|
|
211
215
|
corrector.replace(range, '/')
|
|
212
216
|
elsif range_source.end_with?("\n")
|
|
213
217
|
corrector.replace(range, " #{range_source.strip}\n")
|
|
@@ -28,7 +28,7 @@ module RuboCop
|
|
|
28
28
|
MISTAKES = { '=-' => '-=', '=+' => '+=', '=*' => '*=', '=!' => '!=' }.freeze
|
|
29
29
|
|
|
30
30
|
def on_asgn(node)
|
|
31
|
-
return unless (rhs =
|
|
31
|
+
return unless (rhs = node.expression)
|
|
32
32
|
|
|
33
33
|
range = range_between(node.loc.operator.end_pos - 1, rhs.source_range.begin_pos + 1)
|
|
34
34
|
source = range.source
|
|
@@ -38,16 +38,6 @@ module RuboCop
|
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
SIMPLE_ASSIGNMENT_TYPES.each { |asgn_type| alias_method :"on_#{asgn_type}", :on_asgn }
|
|
41
|
-
|
|
42
|
-
private
|
|
43
|
-
|
|
44
|
-
def rhs(node)
|
|
45
|
-
if node.casgn_type?
|
|
46
|
-
node.children[2]
|
|
47
|
-
else
|
|
48
|
-
node.children[1]
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
41
|
end
|
|
52
42
|
end
|
|
53
43
|
end
|
|
@@ -44,13 +44,6 @@ module RuboCop
|
|
|
44
44
|
RESTRICT_ON_SEND = PRECEDENCE.flatten.freeze
|
|
45
45
|
MSG = 'Wrap expressions with varying precedence with parentheses to avoid ambiguity.'
|
|
46
46
|
|
|
47
|
-
def on_new_investigation
|
|
48
|
-
# Cache the precedence of each node being investigated
|
|
49
|
-
# so that we only need to calculate it once
|
|
50
|
-
@node_precedences = {}
|
|
51
|
-
super
|
|
52
|
-
end
|
|
53
|
-
|
|
54
47
|
def on_and(node)
|
|
55
48
|
return unless (parent = node.parent)
|
|
56
49
|
|
|
@@ -77,9 +70,7 @@ module RuboCop
|
|
|
77
70
|
private
|
|
78
71
|
|
|
79
72
|
def precedence(node)
|
|
80
|
-
|
|
81
|
-
PRECEDENCE.index { |operators| operators.include?(operator_name(node)) }
|
|
82
|
-
end
|
|
73
|
+
PRECEDENCE.index { |operators| operators.include?(operator_name(node)) }
|
|
83
74
|
end
|
|
84
75
|
|
|
85
76
|
def operator?(node)
|
|
@@ -78,13 +78,25 @@ module RuboCop
|
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
def allowed_construct?(asgn_node)
|
|
81
|
-
asgn_node.begin_type?
|
|
81
|
+
return true if asgn_node.begin_type?
|
|
82
|
+
|
|
83
|
+
conditional_assignment?(asgn_node) || discarded_assignment?(asgn_node)
|
|
82
84
|
end
|
|
83
85
|
|
|
84
86
|
def conditional_assignment?(asgn_node)
|
|
85
87
|
!asgn_node.loc.operator
|
|
86
88
|
end
|
|
87
89
|
|
|
90
|
+
# An assignment that is a statement of a multi-statement `begin`
|
|
91
|
+
# (e.g. `(foo = bar; baz)`) has its value discarded, so it is not used
|
|
92
|
+
# as the condition. Wrapping it in parentheses would only conflict with
|
|
93
|
+
# `Style/RedundantParentheses`, so it is left alone.
|
|
94
|
+
def discarded_assignment?(asgn_node)
|
|
95
|
+
parent = asgn_node.parent
|
|
96
|
+
|
|
97
|
+
parent&.begin_type? && parent.children.size > 1
|
|
98
|
+
end
|
|
99
|
+
|
|
88
100
|
def skip_children?(asgn_node)
|
|
89
101
|
(asgn_node.call_type? && !asgn_node.assignment_method?) ||
|
|
90
102
|
empty_condition?(asgn_node) ||
|
|
@@ -80,7 +80,6 @@ module RuboCop
|
|
|
80
80
|
check_assignment_chain(arg_name, arg_value)
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
-
# rubocop:disable Metrics/AbcSize
|
|
84
83
|
def check_assignment_chain(arg_name, node)
|
|
85
84
|
return unless node.lvasgn_type?
|
|
86
85
|
|
|
@@ -88,7 +87,7 @@ module RuboCop
|
|
|
88
87
|
current_node = node
|
|
89
88
|
|
|
90
89
|
while current_node.lvasgn_type?
|
|
91
|
-
seen_variables << current_node.children.first
|
|
90
|
+
seen_variables << current_node.children.first
|
|
92
91
|
current_node = current_node.children.last
|
|
93
92
|
end
|
|
94
93
|
|
|
@@ -99,7 +98,6 @@ module RuboCop
|
|
|
99
98
|
|
|
100
99
|
add_offense(current_node, message: format(MSG, arg_name: arg_name))
|
|
101
100
|
end
|
|
102
|
-
# rubocop:enable Metrics/AbcSize
|
|
103
101
|
end
|
|
104
102
|
end
|
|
105
103
|
end
|
|
@@ -49,7 +49,7 @@ module RuboCop
|
|
|
49
49
|
# Maybe further investigation of RuboCop AST will lead to an essential solution.
|
|
50
50
|
return unless node.loc
|
|
51
51
|
|
|
52
|
-
constant = node.
|
|
52
|
+
constant = node.source.delete_prefix('::')
|
|
53
53
|
return unless (deprecated_constant = deprecated_constants[constant])
|
|
54
54
|
|
|
55
55
|
alternative = deprecated_constant['Alternative']
|
|
@@ -63,12 +63,6 @@ module RuboCop
|
|
|
63
63
|
|
|
64
64
|
private
|
|
65
65
|
|
|
66
|
-
def constant_name(node, nested_constant_name)
|
|
67
|
-
return nested_constant_name.to_s unless node.namespace.const_type?
|
|
68
|
-
|
|
69
|
-
constant_name(node.namespace, "#{node.namespace.short_name}::#{nested_constant_name}")
|
|
70
|
-
end
|
|
71
|
-
|
|
72
66
|
def message(good, bad, deprecated_version)
|
|
73
67
|
deprecated_message = ", deprecated since Ruby #{deprecated_version}" if deprecated_version
|
|
74
68
|
|
|
@@ -77,7 +77,7 @@ module RuboCop
|
|
|
77
77
|
return false unless processed_source.contains_comment?(node.source_range)
|
|
78
78
|
|
|
79
79
|
line_comment = processed_source.comment_at_line(node.source_range.line)
|
|
80
|
-
!line_comment || !comment_disables_cop?(line_comment
|
|
80
|
+
!line_comment || !comment_disables_cop?(line_comment)
|
|
81
81
|
end
|
|
82
82
|
|
|
83
83
|
def allow_empty_lambdas?
|
|
@@ -85,8 +85,8 @@ module RuboCop
|
|
|
85
85
|
end
|
|
86
86
|
|
|
87
87
|
def comment_disables_cop?(comment)
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
directive = DirectiveComment.new(comment)
|
|
89
|
+
directive.disabled? && directive.cop_names.include?(cop_name)
|
|
90
90
|
end
|
|
91
91
|
end
|
|
92
92
|
end
|
|
@@ -43,7 +43,25 @@ module RuboCop
|
|
|
43
43
|
MSG = 'Do not return from an `ensure` block.'
|
|
44
44
|
|
|
45
45
|
def on_ensure(node)
|
|
46
|
-
node.branch&.each_node(:return)
|
|
46
|
+
node.branch&.each_node(:return) do |return_node|
|
|
47
|
+
next if return_from_inner_scope?(return_node, node)
|
|
48
|
+
|
|
49
|
+
add_offense(return_node)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
# A `return` inside a nested method definition or lambda within the
|
|
56
|
+
# `ensure` returns from that inner scope, not from the method whose
|
|
57
|
+
# `ensure` this is, so it is not an offense. A `return` inside a plain
|
|
58
|
+
# block (or `proc`) does propagate out, so it remains an offense.
|
|
59
|
+
def return_from_inner_scope?(return_node, ensure_node)
|
|
60
|
+
return_node.each_ancestor do |ancestor|
|
|
61
|
+
break if ancestor == ensure_node
|
|
62
|
+
return true if ancestor.any_def_type? || (ancestor.any_block_type? && ancestor.lambda?)
|
|
63
|
+
end
|
|
64
|
+
false
|
|
47
65
|
end
|
|
48
66
|
end
|
|
49
67
|
end
|
|
@@ -148,7 +148,9 @@ module RuboCop
|
|
|
148
148
|
arguments = node.arguments
|
|
149
149
|
overridden_kwargs = kwargs.dup
|
|
150
150
|
|
|
151
|
-
|
|
151
|
+
if arguments[2] && !arguments[2].hash_type?
|
|
152
|
+
overridden_kwargs[0] = "trim_mode: #{arguments[2].source}"
|
|
153
|
+
end
|
|
152
154
|
|
|
153
155
|
if arguments[3] && !arguments[3].hash_type?
|
|
154
156
|
overridden_kwargs[1] = "eoutvar: #{arguments[3].source}"
|