rubocop 1.87.0 → 1.88.2
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 +80 -72
- data/config/obsoletion.yml +21 -1
- data/lib/rubocop/cli/command/auto_generate_config.rb +6 -0
- data/lib/rubocop/cop/base.rb +30 -10
- data/lib/rubocop/cop/bundler/gem_comment.rb +5 -3
- data/lib/rubocop/cop/commissioner.rb +13 -11
- 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/duplicated_assignment.rb +12 -2
- 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/comment_indentation.rb +9 -2
- data/lib/rubocop/cop/layout/condition_position.rb +13 -3
- data/lib/rubocop/cop/layout/else_alignment.rb +1 -14
- 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/line_length.rb +10 -5
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +6 -1
- 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 +13 -2
- 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 +10 -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/to_json.rb +8 -1
- 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/unreachable_code.rb +9 -4
- data/lib/rubocop/cop/lint/useless_assignment.rb +10 -5
- data/lib/rubocop/cop/lint/useless_numeric_operation.rb +10 -10
- 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/lint/void.rb +2 -2
- 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/allowed_methods.rb +5 -5
- data/lib/rubocop/cop/mixin/allowed_pattern.rb +5 -1
- data/lib/rubocop/cop/mixin/forbidden_pattern.rb +5 -1
- 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/mixin/statement_modifier.rb +1 -1
- 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/arguments_forwarding.rb +1 -1
- data/lib/rubocop/cop/style/array_first_last.rb +12 -1
- data/lib/rubocop/cop/style/array_intersect.rb +8 -4
- 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/collection_compact.rb +1 -1
- 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/def_with_parentheses.rb +6 -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/documentation_method.rb +5 -1
- 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_conversion.rb +14 -6
- data/lib/rubocop/cop/style/hash_lookup_method.rb +2 -6
- 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/identical_conditional_branches.rb +15 -4
- data/lib/rubocop/cop/style/if_unless_modifier.rb +5 -5
- 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/invertible_unless_condition.rb +25 -5
- 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/lambda_call.rb +11 -0
- 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 +11 -5
- data/lib/rubocop/cop/style/method_def_parentheses.rb +5 -1
- data/lib/rubocop/cop/style/min_max_comparison.rb +3 -0
- data/lib/rubocop/cop/style/missing_respond_to_missing.rb +10 -7
- data/lib/rubocop/cop/style/mixin_usage.rb +1 -1
- data/lib/rubocop/cop/style/module_function.rb +4 -2
- 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_current_directory_in_path.rb +13 -1
- 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_heredoc_delimiter_quotes.rb +6 -1
- 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/struct_inheritance.rb +3 -1
- 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/trivial_accessors.rb +1 -1
- 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/cop/team.rb +18 -4
- data/lib/rubocop/formatter/disabled_config_formatter.rb +14 -7
- data/lib/rubocop/runner.rb +55 -13
- data/lib/rubocop/server/core.rb +6 -0
- data/lib/rubocop/version.rb +1 -1
- metadata +3 -3
|
@@ -121,16 +121,24 @@ module RuboCop
|
|
|
121
121
|
end
|
|
122
122
|
|
|
123
123
|
def multi_argument(node)
|
|
124
|
+
# A splat argument can expand to any number of elements, so the pairs
|
|
125
|
+
# can't be built statically and there is no literal hash to suggest.
|
|
126
|
+
return if node.arguments.any?(&:splat_type?)
|
|
127
|
+
|
|
124
128
|
if node.arguments.count.odd?
|
|
125
129
|
add_offense(node, message: MSG_LITERAL_MULTI_ARG)
|
|
126
130
|
else
|
|
127
|
-
|
|
128
|
-
|
|
131
|
+
correct_multi_argument(node)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
129
134
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
135
|
+
def correct_multi_argument(node)
|
|
136
|
+
add_offense(node, message: MSG_LITERAL_MULTI_ARG) do |corrector|
|
|
137
|
+
corrector.replace(node, args_to_hash(node.arguments))
|
|
138
|
+
|
|
139
|
+
parent = node.parent
|
|
140
|
+
if parent&.send_type? && !parent.method?(:to_h) && !parent.parenthesized?
|
|
141
|
+
add_parentheses(parent, corrector)
|
|
134
142
|
end
|
|
135
143
|
end
|
|
136
144
|
end
|
|
@@ -74,7 +74,7 @@ module RuboCop
|
|
|
74
74
|
|
|
75
75
|
def offense_for_brackets?(node)
|
|
76
76
|
style == :brackets && node.receiver && node.method?(:fetch) && node.arguments.one? &&
|
|
77
|
-
!node.block_literal?
|
|
77
|
+
!node.block_literal? && !node.csend_type?
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
def offense_for_fetch?(node)
|
|
@@ -84,11 +84,7 @@ module RuboCop
|
|
|
84
84
|
def correct_fetch_to_brackets(corrector, node)
|
|
85
85
|
key = node.first_argument.source
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
corrector.replace(node, "(#{node.receiver.source}[#{key}])")
|
|
89
|
-
else
|
|
90
|
-
corrector.replace(node.loc.dot.join(node.source_range.end), "[#{key}]")
|
|
91
|
-
end
|
|
87
|
+
corrector.replace(node.loc.dot.join(node.source_range.end), "[#{key}]")
|
|
92
88
|
end
|
|
93
89
|
|
|
94
90
|
def correct_brackets_to_fetch(corrector, node)
|
|
@@ -19,6 +19,22 @@ module RuboCop
|
|
|
19
19
|
# This cop is unsafe because it cannot be guaranteed that the receiver
|
|
20
20
|
# is a `Hash` or responds to the replacement method.
|
|
21
21
|
#
|
|
22
|
+
# Additionally, the replacement may change the order of the resulting
|
|
23
|
+
# hash: `Hash#slice` returns entries in the order the keys are given,
|
|
24
|
+
# whereas `select`, `filter`, and `reject` preserve the entry order of
|
|
25
|
+
# the receiver.
|
|
26
|
+
#
|
|
27
|
+
# For example:
|
|
28
|
+
#
|
|
29
|
+
# [source,ruby]
|
|
30
|
+
# ----
|
|
31
|
+
# hash = {foo: 1, bar: 2, baz: 3}
|
|
32
|
+
# keys = %i[baz foo]
|
|
33
|
+
#
|
|
34
|
+
# hash.select { |k, _v| keys.include?(k) } # => {foo: 1, baz: 3}
|
|
35
|
+
# hash.slice(*keys) # => {baz: 3, foo: 1}
|
|
36
|
+
# ----
|
|
37
|
+
#
|
|
22
38
|
# @example
|
|
23
39
|
#
|
|
24
40
|
# # bad
|
|
@@ -263,6 +263,8 @@ module RuboCop
|
|
|
263
263
|
|
|
264
264
|
hash_node = pair_node.parent
|
|
265
265
|
return unless hash_node.parent&.return_type? && !hash_node.braces?
|
|
266
|
+
# This runs once per pair, but the hash must only be wrapped once.
|
|
267
|
+
return unless pair_node.equal?(hash_node.pairs.first)
|
|
266
268
|
|
|
267
269
|
corrector.wrap(hash_node, '{', '}')
|
|
268
270
|
end
|
|
@@ -216,23 +216,34 @@ module RuboCop
|
|
|
216
216
|
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
217
217
|
|
|
218
218
|
def correct_assignment(corrector, node, expression, insert_position)
|
|
219
|
+
indentation = indentation_of(node.parent)
|
|
220
|
+
|
|
219
221
|
if insert_position == :after_condition
|
|
220
222
|
assignment = node.parent.source_range.with(end_pos: node.source_range.begin_pos)
|
|
221
223
|
corrector.remove(assignment)
|
|
222
|
-
corrector.insert_after(node, "\n#{assignment.source}#{expression.source}")
|
|
224
|
+
corrector.insert_after(node, "\n#{indentation}#{assignment.source}#{expression.source}")
|
|
223
225
|
else
|
|
224
|
-
corrector.insert_before(node.parent, "#{expression.source}\n")
|
|
226
|
+
corrector.insert_before(node.parent, "#{expression.source}\n#{indentation}")
|
|
225
227
|
end
|
|
226
228
|
end
|
|
227
229
|
|
|
228
230
|
def correct_no_assignment(corrector, node, expression, insert_position)
|
|
231
|
+
indentation = indentation_of(node)
|
|
232
|
+
|
|
229
233
|
if insert_position == :after_condition
|
|
230
|
-
corrector.insert_after(node, "\n#{expression.source}")
|
|
234
|
+
corrector.insert_after(node, "\n#{indentation}#{expression.source}")
|
|
231
235
|
else
|
|
232
|
-
corrector.insert_before(node, "#{expression.source}\n")
|
|
236
|
+
corrector.insert_before(node, "#{expression.source}\n#{indentation}")
|
|
233
237
|
end
|
|
234
238
|
end
|
|
235
239
|
|
|
240
|
+
# The leading indentation of the line the conditional starts on, so a
|
|
241
|
+
# hoisted expression keeps the surrounding nesting instead of landing
|
|
242
|
+
# at column zero.
|
|
243
|
+
def indentation_of(node)
|
|
244
|
+
' ' * node.source_range.column
|
|
245
|
+
end
|
|
246
|
+
|
|
236
247
|
def last_child_of_parent?(node)
|
|
237
248
|
return true unless (parent = node.parent)
|
|
238
249
|
|
|
@@ -86,12 +86,12 @@ module RuboCop
|
|
|
86
86
|
MSG_USE_NORMAL = 'Modifier form of `%<keyword>s` makes the line too long.'
|
|
87
87
|
|
|
88
88
|
def self.autocorrect_incompatible_with
|
|
89
|
-
[Style::SoleNestedConditional]
|
|
89
|
+
[Style::Next, Style::SoleNestedConditional]
|
|
90
90
|
end
|
|
91
91
|
|
|
92
92
|
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
93
93
|
def on_if(node)
|
|
94
|
-
return if endless_method?(node.body) || node.
|
|
94
|
+
return if endless_method?(node.body) || node.each_ancestor(:dstr).any?
|
|
95
95
|
|
|
96
96
|
condition = node.condition
|
|
97
97
|
return if defined_nodes(condition).any? { |n| defined_argument_is_undefined?(node, n) } ||
|
|
@@ -119,7 +119,7 @@ module RuboCop
|
|
|
119
119
|
if condition.defined_type?
|
|
120
120
|
[condition]
|
|
121
121
|
else
|
|
122
|
-
condition.each_descendant
|
|
122
|
+
condition.each_descendant(:defined?)
|
|
123
123
|
end
|
|
124
124
|
end
|
|
125
125
|
|
|
@@ -137,7 +137,7 @@ module RuboCop
|
|
|
137
137
|
if condition.any_match_pattern_type?
|
|
138
138
|
[condition]
|
|
139
139
|
else
|
|
140
|
-
condition.each_descendant
|
|
140
|
+
condition.each_descendant(:any_match_pattern)
|
|
141
141
|
end
|
|
142
142
|
end
|
|
143
143
|
|
|
@@ -350,7 +350,7 @@ module RuboCop
|
|
|
350
350
|
end
|
|
351
351
|
|
|
352
352
|
def comment_on_node_line(node)
|
|
353
|
-
processed_source.
|
|
353
|
+
processed_source.comment_at_line(node.first_line)
|
|
354
354
|
end
|
|
355
355
|
|
|
356
356
|
def remove_comment(corrector, _node, comment)
|
|
@@ -82,7 +82,7 @@ module RuboCop
|
|
|
82
82
|
|
|
83
83
|
then_code, else_code = else_code, then_code if node.unless?
|
|
84
84
|
|
|
85
|
-
"#{node
|
|
85
|
+
"#{ternary_condition(node)} ? #{then_code} : #{else_code}"
|
|
86
86
|
end
|
|
87
87
|
|
|
88
88
|
def correct_elsif(node)
|
|
@@ -103,6 +103,14 @@ module RuboCop
|
|
|
103
103
|
"#{method.source}(#{arguments.source})"
|
|
104
104
|
end
|
|
105
105
|
|
|
106
|
+
# An assignment used as the condition must be parenthesized, otherwise the
|
|
107
|
+
# assignment would capture the whole ternary (`a = b ? c : d` instead of
|
|
108
|
+
# `(a = b) ? c : d`), changing what gets assigned.
|
|
109
|
+
def ternary_condition(node)
|
|
110
|
+
condition = node.condition
|
|
111
|
+
condition.assignment? ? "(#{condition.source})" : condition.source
|
|
112
|
+
end
|
|
113
|
+
|
|
106
114
|
def build_else_branch(second_condition)
|
|
107
115
|
result = <<~RUBY
|
|
108
116
|
elsif #{second_condition.condition.source}
|
|
@@ -26,7 +26,7 @@ module RuboCop
|
|
|
26
26
|
def on_new_investigation
|
|
27
27
|
processed_source.comments.each do |comment|
|
|
28
28
|
next if comment_line?(processed_source[comment.loc.line - 1]) ||
|
|
29
|
-
comment.text.match?(/\A# rubocop:(enable|disable)/)
|
|
29
|
+
comment.text.match?(/\A# rubocop:(enable|disable|todo)/)
|
|
30
30
|
|
|
31
31
|
add_offense(comment)
|
|
32
32
|
end
|
|
@@ -75,7 +75,9 @@ module RuboCop
|
|
|
75
75
|
def invertible?(node) # rubocop:disable Metrics/CyclomaticComplexity
|
|
76
76
|
case node&.type
|
|
77
77
|
when :begin
|
|
78
|
-
|
|
78
|
+
# A multi-statement `begin` evaluates to its last expression, so it
|
|
79
|
+
# cannot be inverted by negating a single child.
|
|
80
|
+
node.children.one? && invertible?(node.children.first)
|
|
79
81
|
when :send
|
|
80
82
|
return false if inheritance_check?(node)
|
|
81
83
|
|
|
@@ -124,12 +126,17 @@ module RuboCop
|
|
|
124
126
|
end
|
|
125
127
|
|
|
126
128
|
def preferred_logical_condition(node)
|
|
127
|
-
preferred_lhs =
|
|
128
|
-
preferred_rhs =
|
|
129
|
+
preferred_lhs = preferred_operand(node, node.lhs)
|
|
130
|
+
preferred_rhs = preferred_operand(node, node.rhs)
|
|
129
131
|
|
|
130
132
|
"#{preferred_lhs} #{node.inverse_operator} #{preferred_rhs}"
|
|
131
133
|
end
|
|
132
134
|
|
|
135
|
+
def preferred_operand(node, operand)
|
|
136
|
+
preferred = preferred_condition(operand)
|
|
137
|
+
parenthesize_inverted_operand?(node, operand) ? "(#{preferred})" : preferred
|
|
138
|
+
end
|
|
139
|
+
|
|
133
140
|
def autocorrect(corrector, node)
|
|
134
141
|
case node.type
|
|
135
142
|
when :begin
|
|
@@ -138,11 +145,24 @@ module RuboCop
|
|
|
138
145
|
autocorrect_send_node(corrector, node)
|
|
139
146
|
when :or, :and
|
|
140
147
|
corrector.replace(node.loc.operator, node.inverse_operator)
|
|
141
|
-
|
|
142
|
-
|
|
148
|
+
autocorrect_operand(corrector, node, node.lhs)
|
|
149
|
+
autocorrect_operand(corrector, node, node.rhs)
|
|
143
150
|
end
|
|
144
151
|
end
|
|
145
152
|
|
|
153
|
+
def autocorrect_operand(corrector, node, operand)
|
|
154
|
+
autocorrect(corrector, operand)
|
|
155
|
+
corrector.wrap(operand, '(', ')') if parenthesize_inverted_operand?(node, operand)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# When an `and` is nested in an `or`, inverting both operators turns the
|
|
159
|
+
# `and` into an `or` that now binds looser than its parent, so it must be
|
|
160
|
+
# parenthesized to keep the original grouping (`a && b || c` inverts to
|
|
161
|
+
# `(!a || !b) && !c`, not `!a || !b && !c`).
|
|
162
|
+
def parenthesize_inverted_operand?(node, operand)
|
|
163
|
+
node.or_type? && operand.and_type?
|
|
164
|
+
end
|
|
165
|
+
|
|
146
166
|
def autocorrect_send_node(corrector, node)
|
|
147
167
|
if node.method?(:!)
|
|
148
168
|
corrector.remove(node.loc.selector)
|
|
@@ -37,6 +37,10 @@ module RuboCop
|
|
|
37
37
|
return unless (ancestor = node.parent&.parent)
|
|
38
38
|
|
|
39
39
|
merge_kwargs?(ancestor) do |merge_node, hash_node, other_hash_node|
|
|
40
|
+
# A block-pass argument (e.g. `merge(other, &block)`) has no keyword
|
|
41
|
+
# equivalent, so spreading it would produce invalid Ruby (`**&block`).
|
|
42
|
+
next if other_hash_node.any?(&:block_pass_type?)
|
|
43
|
+
|
|
40
44
|
add_offense(merge_node) do |corrector|
|
|
41
45
|
autocorrect(corrector, node, hash_node, other_hash_node)
|
|
42
46
|
end
|
|
@@ -62,11 +62,15 @@ module RuboCop
|
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
def append_newline_to_last_kwoptarg(arguments, corrector)
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
# The newline only needs restoring when the moved keyword argument was
|
|
66
|
+
# the last parameter, so removing it also consumes the line break before
|
|
67
|
+
# the body. When a `kwoptarg` already trails the list, the body stays
|
|
68
|
+
# separated and inserting a newline would leave a spurious blank line.
|
|
69
|
+
return unless arguments.last.kwarg_type?
|
|
70
|
+
return if arguments.parent.block_type?
|
|
67
71
|
|
|
68
72
|
last_kwoptarg = arguments.reverse.find(&:kwoptarg_type?)
|
|
69
|
-
corrector.insert_after(last_kwoptarg, "\n")
|
|
73
|
+
corrector.insert_after(last_kwoptarg, "\n")
|
|
70
74
|
end
|
|
71
75
|
|
|
72
76
|
def remove_kwargs(kwarg_nodes, corrector)
|
|
@@ -118,7 +118,13 @@ module RuboCop
|
|
|
118
118
|
end
|
|
119
119
|
|
|
120
120
|
def lambda_arg_string(args)
|
|
121
|
-
|
|
121
|
+
# Block-local (shadow) arguments are separated from regular arguments by a
|
|
122
|
+
# `;`; joining everything with `,` would turn them into extra parameters
|
|
123
|
+
# and change the lambda's arity.
|
|
124
|
+
regular, shadow = args.children.partition { |arg| !arg.shadowarg_type? }
|
|
125
|
+
arg_string = regular.map(&:source).join(', ')
|
|
126
|
+
arg_string += "; #{shadow.map(&:source).join(', ')}" unless shadow.empty?
|
|
127
|
+
arg_string
|
|
122
128
|
end
|
|
123
129
|
end
|
|
124
130
|
end
|
|
@@ -27,6 +27,9 @@ module RuboCop
|
|
|
27
27
|
|
|
28
28
|
def on_send(node)
|
|
29
29
|
return unless node.receiver
|
|
30
|
+
# Rewriting the call rebuilds it as a single expression, which would drop
|
|
31
|
+
# any comments inside the argument list, so leave it to be fixed manually.
|
|
32
|
+
return if comments_in_node?(node)
|
|
30
33
|
|
|
31
34
|
if offense?(node)
|
|
32
35
|
prefer = prefer(node)
|
|
@@ -48,6 +51,14 @@ module RuboCop
|
|
|
48
51
|
|
|
49
52
|
private
|
|
50
53
|
|
|
54
|
+
def comments_in_node?(node)
|
|
55
|
+
range = node.source_range
|
|
56
|
+
processed_source.comments.any? do |comment|
|
|
57
|
+
range.begin_pos <= comment.source_range.begin_pos &&
|
|
58
|
+
comment.source_range.end_pos <= range.end_pos
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
51
62
|
def offense?(node)
|
|
52
63
|
(explicit_style? && node.implicit_call?) || (implicit_style? && !node.implicit_call?)
|
|
53
64
|
end
|
|
@@ -6,6 +6,17 @@ module RuboCop
|
|
|
6
6
|
# Prefer `select` or `reject` over `map { ... }.compact`.
|
|
7
7
|
# This cop also handles `filter_map { ... }`, similar to `map { ... }.compact`.
|
|
8
8
|
#
|
|
9
|
+
# @safety
|
|
10
|
+
# This cop is unsafe because `compact` also removes `nil` elements that
|
|
11
|
+
# were already present in the receiver, whereas `select`/`reject` keep
|
|
12
|
+
# them. The result therefore differs when the collection contains `nil`:
|
|
13
|
+
#
|
|
14
|
+
# [source,ruby]
|
|
15
|
+
# ----
|
|
16
|
+
# [nil, 1].map { |e| e if e }.compact # => [1]
|
|
17
|
+
# [nil, 1].select { |e| e } # => [nil, 1]
|
|
18
|
+
# ----
|
|
19
|
+
#
|
|
9
20
|
# @example
|
|
10
21
|
#
|
|
11
22
|
# # bad
|
|
@@ -65,7 +65,7 @@ module RuboCop
|
|
|
65
65
|
|
|
66
66
|
# @!method suitable_argument_node?(node)
|
|
67
67
|
def_node_matcher :suitable_argument_node?, <<-PATTERN
|
|
68
|
-
!{splat forwarded-restarg forwarded-args (hash (forwarded-kwrestarg))
|
|
68
|
+
!{splat forwarded-restarg forwarded-args (hash (forwarded-kwrestarg)) block-pass}
|
|
69
69
|
PATTERN
|
|
70
70
|
|
|
71
71
|
# @!method each_block_with_push?(node)
|
|
@@ -50,9 +50,11 @@ module RuboCop
|
|
|
50
50
|
private
|
|
51
51
|
|
|
52
52
|
def register_offense(node)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
range = offense_range(node)
|
|
54
|
+
return if processed_source.contains_comment?(range)
|
|
55
|
+
|
|
56
|
+
add_offense(range) do |corrector|
|
|
57
|
+
corrector.remove(range)
|
|
56
58
|
end
|
|
57
59
|
end
|
|
58
60
|
|
|
@@ -88,8 +90,12 @@ module RuboCop
|
|
|
88
90
|
#
|
|
89
91
|
def parenthesized_it_method_in_block?(node)
|
|
90
92
|
return false unless node.method?(:it)
|
|
91
|
-
return false unless (block_node = node.each_ancestor(:
|
|
92
|
-
|
|
93
|
+
return false unless (block_node = node.each_ancestor(:any_block).first)
|
|
94
|
+
# Inside a numbered/`it` block, a bare `it` is a parse error (it conflicts
|
|
95
|
+
# with the implicit parameter), so `it()` must keep its parentheses.
|
|
96
|
+
if block_node.block_type? && !block_node.arguments.empty_and_without_delimiters?
|
|
97
|
+
return false
|
|
98
|
+
end
|
|
93
99
|
|
|
94
100
|
!node.receiver && node.arguments.empty? && !node.block_literal?
|
|
95
101
|
end
|
|
@@ -102,6 +102,10 @@ module RuboCop
|
|
|
102
102
|
MSG_PRESENT = 'Use def without parentheses.'
|
|
103
103
|
MSG_MISSING = 'Use def with parentheses when there are parameters.'
|
|
104
104
|
|
|
105
|
+
def self.autocorrect_incompatible_with
|
|
106
|
+
[Style::ArgumentsForwarding]
|
|
107
|
+
end
|
|
108
|
+
|
|
105
109
|
def on_def(node)
|
|
106
110
|
args = node.arguments
|
|
107
111
|
|
|
@@ -166,7 +170,7 @@ module RuboCop
|
|
|
166
170
|
|
|
167
171
|
def anonymous_arguments?(node)
|
|
168
172
|
return true if node.arguments.any? do |arg|
|
|
169
|
-
arg.type?(:
|
|
173
|
+
arg.forward_arg_type? || (arg.type?(:restarg, :kwrestarg) && arg.name.nil?)
|
|
170
174
|
end
|
|
171
175
|
return false unless (last_argument = node.last_argument)
|
|
172
176
|
|
|
@@ -55,8 +55,11 @@ module RuboCop
|
|
|
55
55
|
lhs, operator, rhs = comparison_condition(node.condition)
|
|
56
56
|
return unless operator
|
|
57
57
|
|
|
58
|
+
# For `unless`, the branches run opposite to an `if`, so swap them to
|
|
59
|
+
# keep the `max`/`min` decision correct.
|
|
58
60
|
if_branch = node.if_branch
|
|
59
61
|
else_branch = node.else_branch
|
|
62
|
+
if_branch, else_branch = else_branch, if_branch if node.unless?
|
|
60
63
|
preferred_method = preferred_method(operator, lhs, rhs, if_branch, else_branch)
|
|
61
64
|
return unless preferred_method
|
|
62
65
|
|
|
@@ -65,16 +65,19 @@ module RuboCop
|
|
|
65
65
|
private
|
|
66
66
|
|
|
67
67
|
def implements_respond_to_missing?(node)
|
|
68
|
-
|
|
68
|
+
scope = enclosing_scope(node)
|
|
69
|
+
search_root = scope || node.parent
|
|
70
|
+
return false unless search_root
|
|
69
71
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
child = descendant.children.first
|
|
74
|
-
return true if child.respond_to?(:method?) && child.method?(:respond_to_missing?)
|
|
72
|
+
search_root.each_descendant(node.type).any? do |descendant|
|
|
73
|
+
descendant.method?(:respond_to_missing?) && enclosing_scope(descendant).equal?(scope)
|
|
75
74
|
end
|
|
75
|
+
end
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
# The class/module/`class << self` body that lexically contains `node`,
|
|
78
|
+
# or `nil` when `node` is defined at the top level.
|
|
79
|
+
def enclosing_scope(node)
|
|
80
|
+
node.each_ancestor(:class, :module, :sclass).first
|
|
78
81
|
end
|
|
79
82
|
end
|
|
80
83
|
end
|
|
@@ -110,9 +110,11 @@ module RuboCop
|
|
|
110
110
|
def_node_matcher :private_directive?, '(send nil? :private ...)'
|
|
111
111
|
|
|
112
112
|
def on_module(node)
|
|
113
|
-
return unless node.body
|
|
113
|
+
return unless node.body
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
body_nodes = node.body.begin_type? ? node.body.children : [node.body]
|
|
116
|
+
|
|
117
|
+
each_wrong_style(body_nodes) do |child_node|
|
|
116
118
|
add_offense(child_node) do |corrector|
|
|
117
119
|
next if style == :forbidden
|
|
118
120
|
|
|
@@ -65,10 +65,16 @@ module RuboCop
|
|
|
65
65
|
if style == :keyword
|
|
66
66
|
rhs.begin_type?
|
|
67
67
|
else
|
|
68
|
-
|
|
68
|
+
# A `begin` block with `rescue`/`ensure` cannot be expressed with
|
|
69
|
+
# parentheses, so wrapping it in `(` and `)` is not possible.
|
|
70
|
+
rhs.kwbegin_type? && !contains_rescue_or_ensure?(rhs)
|
|
69
71
|
end
|
|
70
72
|
end
|
|
71
73
|
|
|
74
|
+
def contains_rescue_or_ensure?(node)
|
|
75
|
+
node.each_child_node(:rescue, :ensure).any?
|
|
76
|
+
end
|
|
77
|
+
|
|
72
78
|
def keyword_autocorrect(node, corrector)
|
|
73
79
|
node_buf = node.source_range.source_buffer
|
|
74
80
|
corrector.replace(node.loc.begin, keyword_begin_str(node, node_buf))
|
|
@@ -51,10 +51,12 @@ module RuboCop
|
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
arguments_range = range_with_surrounding_space(arguments_range(node), side: :left)
|
|
54
|
-
# If the method name isn't on the same line as def
|
|
54
|
+
# If the method name isn't on the same line as `def`, pull the name and
|
|
55
|
+
# the opening parenthesis up next to `def` so the collapsed signature
|
|
56
|
+
# stays on a single line and remains valid Ruby.
|
|
55
57
|
if arguments_range.first_line != opening_line(node)
|
|
56
|
-
|
|
57
|
-
corrector.
|
|
58
|
+
prefix_range = range_between(node.loc.keyword.end_pos, begin_of_arguments.begin_pos)
|
|
59
|
+
corrector.replace(prefix_range, " #{prefix_range.source.strip}")
|
|
58
60
|
end
|
|
59
61
|
|
|
60
62
|
corrector.remove(arguments_range)
|
|
@@ -85,7 +87,12 @@ module RuboCop
|
|
|
85
87
|
end
|
|
86
88
|
|
|
87
89
|
def definition_width(node)
|
|
88
|
-
|
|
90
|
+
# Measure the collapsed single-line width the autocorrect would
|
|
91
|
+
# produce, not the multi-line source length, so a signature that
|
|
92
|
+
# would fit on one line is not skipped.
|
|
93
|
+
signature = node.source_range.begin.join(node.arguments.source_range.end).source
|
|
94
|
+
|
|
95
|
+
signature.gsub(/\s+/, ' ').length
|
|
89
96
|
end
|
|
90
97
|
end
|
|
91
98
|
end
|