rubocop 1.67.0 → 1.69.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/config/default.yml +81 -6
- data/lib/rubocop/cached_data.rb +12 -4
- data/lib/rubocop/cli/command/execute_runner.rb +1 -1
- data/lib/rubocop/cli/command/version.rb +2 -2
- data/lib/rubocop/cop/autocorrect_logic.rb +22 -2
- data/lib/rubocop/cop/base.rb +1 -1
- data/lib/rubocop/cop/bundler/gem_filename.rb +0 -1
- data/lib/rubocop/cop/bundler/insecure_protocol_source.rb +0 -1
- data/lib/rubocop/cop/correctors/alignment_corrector.rb +1 -12
- data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +1 -1
- data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +10 -0
- data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +1 -2
- data/lib/rubocop/cop/gemspec/required_ruby_version.rb +0 -2
- data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +2 -4
- data/lib/rubocop/cop/internal_affairs/numblock_handler.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/operator_keyword.rb +46 -0
- data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +0 -2
- data/lib/rubocop/cop/internal_affairs.rb +1 -0
- data/lib/rubocop/cop/layout/argument_alignment.rb +1 -2
- data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/begin_end_alignment.rb +0 -1
- data/lib/rubocop/cop/layout/block_alignment.rb +1 -2
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +2 -3
- data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +3 -4
- data/lib/rubocop/cop/layout/empty_lines_around_method_body.rb +3 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +7 -7
- data/lib/rubocop/cop/layout/leading_comment_space.rb +44 -1
- data/lib/rubocop/cop/layout/line_length.rb +118 -4
- data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +1 -1
- data/lib/rubocop/cop/layout/multiline_method_definition_brace_layout.rb +1 -1
- data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -3
- data/lib/rubocop/cop/layout/parameter_alignment.rb +3 -4
- data/lib/rubocop/cop/layout/redundant_line_break.rb +3 -35
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +3 -2
- data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_operators.rb +16 -17
- data/lib/rubocop/cop/layout/space_before_brackets.rb +5 -5
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +6 -0
- data/lib/rubocop/cop/layout/space_inside_block_braces.rb +4 -0
- data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +4 -0
- data/lib/rubocop/cop/layout/space_inside_string_interpolation.rb +0 -1
- data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +11 -12
- data/lib/rubocop/cop/lint/circular_argument_reference.rb +2 -0
- data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +1 -1
- data/lib/rubocop/cop/lint/duplicate_branch.rb +39 -4
- data/lib/rubocop/cop/lint/empty_ensure.rb +1 -1
- data/lib/rubocop/cop/lint/empty_file.rb +0 -2
- data/lib/rubocop/cop/lint/ensure_return.rb +1 -1
- data/lib/rubocop/cop/lint/float_comparison.rb +14 -6
- data/lib/rubocop/cop/lint/float_out_of_range.rb +1 -3
- data/lib/rubocop/cop/lint/hash_new_with_keyword_arguments_as_default.rb +55 -0
- data/lib/rubocop/cop/lint/interpolation_check.rb +9 -0
- data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +3 -0
- data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +1 -1
- data/lib/rubocop/cop/lint/mixed_case_range.rb +2 -5
- data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
- data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +2 -2
- data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +8 -1
- data/lib/rubocop/cop/lint/number_conversion.rb +0 -1
- data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -2
- data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +106 -0
- data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +1 -2
- data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +12 -7
- data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +8 -7
- data/lib/rubocop/cop/lint/regexp_as_condition.rb +0 -1
- data/lib/rubocop/cop/lint/rescue_type.rb +3 -7
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +9 -0
- data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +5 -1
- data/lib/rubocop/cop/lint/self_assignment.rb +8 -10
- data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -1
- data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +88 -0
- data/lib/rubocop/cop/lint/unused_method_argument.rb +18 -2
- data/lib/rubocop/cop/lint/useless_defined.rb +55 -0
- data/lib/rubocop/cop/lint/useless_rescue.rb +1 -1
- data/lib/rubocop/cop/lint/useless_setter_call.rb +14 -25
- data/lib/rubocop/cop/lint/void.rb +3 -2
- data/lib/rubocop/cop/metrics/class_length.rb +7 -7
- data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +4 -1
- data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +1 -1
- data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -2
- data/lib/rubocop/cop/mixin/check_assignment.rb +4 -12
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +10 -0
- data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +49 -0
- data/lib/rubocop/cop/mixin/dig_help.rb +27 -0
- data/lib/rubocop/cop/mixin/endless_method_rewriter.rb +24 -0
- data/lib/rubocop/cop/mixin/frozen_string_literal.rb +3 -1
- data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +5 -9
- data/lib/rubocop/cop/mixin/range_help.rb +0 -1
- data/lib/rubocop/cop/mixin/target_ruby_version.rb +17 -1
- data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
- data/lib/rubocop/cop/naming/constant_name.rb +6 -7
- data/lib/rubocop/cop/naming/file_name.rb +0 -2
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +11 -12
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +3 -11
- data/lib/rubocop/cop/naming/variable_name.rb +3 -4
- data/lib/rubocop/cop/naming/variable_number.rb +2 -3
- data/lib/rubocop/cop/offense.rb +2 -3
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +53 -24
- data/lib/rubocop/cop/style/ambiguous_endless_method_definition.rb +79 -0
- data/lib/rubocop/cop/style/array_intersect.rb +5 -4
- data/lib/rubocop/cop/style/bitwise_predicate.rb +100 -0
- data/lib/rubocop/cop/style/block_delimiters.rb +18 -3
- data/lib/rubocop/cop/style/case_like_if.rb +8 -11
- data/lib/rubocop/cop/style/combinable_defined.rb +115 -0
- data/lib/rubocop/cop/style/commented_keyword.rb +11 -1
- data/lib/rubocop/cop/style/conditional_assignment.rb +19 -21
- data/lib/rubocop/cop/style/constant_visibility.rb +3 -12
- data/lib/rubocop/cop/style/dig_chain.rb +90 -0
- data/lib/rubocop/cop/style/endless_method.rb +1 -14
- data/lib/rubocop/cop/style/file_null.rb +73 -0
- data/lib/rubocop/cop/style/file_touch.rb +75 -0
- data/lib/rubocop/cop/style/for.rb +0 -1
- data/lib/rubocop/cop/style/global_vars.rb +1 -3
- data/lib/rubocop/cop/style/guard_clause.rb +15 -2
- data/lib/rubocop/cop/style/hash_conversion.rb +1 -2
- data/lib/rubocop/cop/style/if_inside_else.rb +0 -1
- data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +1 -2
- data/lib/rubocop/cop/style/if_with_semicolon.rb +14 -5
- data/lib/rubocop/cop/style/inverse_methods.rb +0 -1
- data/lib/rubocop/cop/style/keyword_arguments_merging.rb +67 -0
- data/lib/rubocop/cop/style/lambda_call.rb +0 -1
- data/lib/rubocop/cop/style/map_into_array.rb +6 -1
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +7 -11
- data/lib/rubocop/cop/style/missing_respond_to_missing.rb +33 -3
- data/lib/rubocop/cop/style/multiline_memoization.rb +1 -1
- data/lib/rubocop/cop/style/multiple_comparison.rb +28 -39
- data/lib/rubocop/cop/style/mutable_constant.rb +4 -5
- data/lib/rubocop/cop/style/negated_if_else_condition.rb +6 -4
- data/lib/rubocop/cop/style/nested_ternary_operator.rb +5 -4
- data/lib/rubocop/cop/style/not.rb +1 -1
- data/lib/rubocop/cop/style/one_line_conditional.rb +25 -4
- data/lib/rubocop/cop/style/operator_method_call.rb +5 -6
- data/lib/rubocop/cop/style/or_assignment.rb +3 -6
- data/lib/rubocop/cop/style/parallel_assignment.rb +8 -13
- data/lib/rubocop/cop/style/raise_args.rb +1 -1
- data/lib/rubocop/cop/style/redundant_assignment.rb +1 -1
- data/lib/rubocop/cop/style/redundant_condition.rb +36 -21
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +21 -2
- data/lib/rubocop/cop/style/redundant_parentheses.rb +9 -11
- data/lib/rubocop/cop/style/redundant_regexp_argument.rb +1 -0
- data/lib/rubocop/cop/style/redundant_return.rb +2 -2
- data/lib/rubocop/cop/style/redundant_self.rb +7 -14
- data/lib/rubocop/cop/style/redundant_self_assignment.rb +7 -5
- data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +4 -4
- data/lib/rubocop/cop/style/redundant_sort.rb +1 -1
- data/lib/rubocop/cop/style/rescue_modifier.rb +2 -3
- data/lib/rubocop/cop/style/safe_navigation.rb +13 -1
- data/lib/rubocop/cop/style/safe_navigation_chain_length.rb +52 -0
- data/lib/rubocop/cop/style/select_by_regexp.rb +1 -1
- data/lib/rubocop/cop/style/self_assignment.rb +11 -17
- data/lib/rubocop/cop/style/signal_exception.rb +2 -3
- data/lib/rubocop/cop/style/single_argument_dig.rb +9 -5
- data/lib/rubocop/cop/style/single_line_do_end_block.rb +13 -3
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +2 -3
- data/lib/rubocop/cop/style/special_global_vars.rb +1 -1
- data/lib/rubocop/cop/style/string_concatenation.rb +0 -1
- data/lib/rubocop/cop/style/swap_values.rb +4 -15
- data/lib/rubocop/cop/style/ternary_parentheses.rb +25 -4
- data/lib/rubocop/cop/style/trailing_underscore_variable.rb +4 -4
- data/lib/rubocop/cop/style/variable_interpolation.rb +1 -2
- data/lib/rubocop/cop/variable_force/assignment.rb +18 -3
- data/lib/rubocop/cop/variable_force/branch.rb +1 -1
- data/lib/rubocop/cop/variable_force/variable.rb +5 -1
- data/lib/rubocop/cop/variable_force/variable_table.rb +2 -2
- data/lib/rubocop/cop/variable_force.rb +4 -10
- data/lib/rubocop/cops_documentation_generator.rb +20 -10
- data/lib/rubocop/formatter/disabled_config_formatter.rb +1 -1
- data/lib/rubocop/runner.rb +16 -8
- data/lib/rubocop/target_ruby.rb +1 -1
- data/lib/rubocop/version.rb +27 -8
- data/lib/rubocop.rb +16 -0
- metadata +28 -12
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Checks for usage of `File.open` in append mode with empty block.
|
7
|
+
#
|
8
|
+
# Such a usage only creates a new file, but it doesn't update
|
9
|
+
# timestamps for an existing file, which might have been the intention.
|
10
|
+
#
|
11
|
+
# For example, for an existing file `foo.txt`:
|
12
|
+
#
|
13
|
+
# ruby -e "puts File.mtime('foo.txt')"
|
14
|
+
# # 2024-11-26 12:17:23 +0100
|
15
|
+
#
|
16
|
+
# ruby -e "File.open('foo.txt', 'a') {}"
|
17
|
+
#
|
18
|
+
# ruby -e "puts File.mtime('foo.txt')"
|
19
|
+
# # 2024-11-26 12:17:23 +0100 -> unchanged
|
20
|
+
#
|
21
|
+
# If the intention was to update timestamps, `FileUtils.touch('foo.txt')`
|
22
|
+
# should be used instead.
|
23
|
+
#
|
24
|
+
# @safety
|
25
|
+
# Autocorrection is unsafe for this cop because unlike `File.open`,
|
26
|
+
# `FileUtils.touch` updates an existing file's timestamps.
|
27
|
+
#
|
28
|
+
# @example
|
29
|
+
# # bad
|
30
|
+
# File.open(filename, 'a') {}
|
31
|
+
# File.open(filename, 'a+') {}
|
32
|
+
#
|
33
|
+
# # good
|
34
|
+
# FileUtils.touch(filename)
|
35
|
+
#
|
36
|
+
class FileTouch < Base
|
37
|
+
extend AutoCorrector
|
38
|
+
|
39
|
+
MSG = 'Use `FileUtils.touch(%<argument>s)` instead of `File.open` in ' \
|
40
|
+
'append mode with empty block.'
|
41
|
+
|
42
|
+
RESTRICT_ON_SEND = %i[open].freeze
|
43
|
+
|
44
|
+
APPEND_FILE_MODES = %w[a a+ ab a+b at a+t].to_set.freeze
|
45
|
+
|
46
|
+
# @!method file_open?(node)
|
47
|
+
def_node_matcher :file_open?, <<~PATTERN
|
48
|
+
(send
|
49
|
+
(const {nil? cbase} :File) :open
|
50
|
+
$(...)
|
51
|
+
(str %APPEND_FILE_MODES))
|
52
|
+
PATTERN
|
53
|
+
|
54
|
+
def on_send(node)
|
55
|
+
filename = file_open?(node)
|
56
|
+
parent = node.parent
|
57
|
+
|
58
|
+
return unless filename
|
59
|
+
return unless parent && empty_block?(parent)
|
60
|
+
|
61
|
+
message = format(MSG, argument: filename.source)
|
62
|
+
add_offense(parent, message: message) do |corrector|
|
63
|
+
corrector.replace(parent, "FileUtils.touch(#{filename.source})")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def empty_block?(node)
|
70
|
+
node.block_type? && !node.body
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -264,7 +264,7 @@ module RuboCop
|
|
264
264
|
|
265
265
|
def and_or_guard_clause?(guard_clause)
|
266
266
|
parent = guard_clause.parent
|
267
|
-
parent.
|
267
|
+
parent.operator_keyword?
|
268
268
|
end
|
269
269
|
|
270
270
|
def too_long_for_single_line?(node, example)
|
@@ -283,7 +283,8 @@ module RuboCop
|
|
283
283
|
end
|
284
284
|
|
285
285
|
def accepted_if?(node, ending)
|
286
|
-
return true if node.modifier_form? || node.ternary? || node.elsif_conditional?
|
286
|
+
return true if node.modifier_form? || node.ternary? || node.elsif_conditional? ||
|
287
|
+
assigned_lvar_used_in_if_branch?(node)
|
287
288
|
|
288
289
|
if ending
|
289
290
|
node.else?
|
@@ -292,6 +293,18 @@ module RuboCop
|
|
292
293
|
end
|
293
294
|
end
|
294
295
|
|
296
|
+
def assigned_lvar_used_in_if_branch?(node)
|
297
|
+
return false unless (if_branch = node.if_branch)
|
298
|
+
|
299
|
+
assigned_lvars_in_condition = node.condition.each_descendant(:lvasgn).map do |lvasgn|
|
300
|
+
lvar_name, = *lvasgn
|
301
|
+
lvar_name.to_s
|
302
|
+
end
|
303
|
+
used_lvars_in_branch = if_branch.each_descendant(:lvar).map(&:source) || []
|
304
|
+
|
305
|
+
(assigned_lvars_in_condition & used_lvars_in_branch).any?
|
306
|
+
end
|
307
|
+
|
295
308
|
def remove_whole_lines(corrector, range)
|
296
309
|
corrector.remove(range_by_whole_lines(range, include_final_newline: true))
|
297
310
|
end
|
@@ -111,8 +111,7 @@ module RuboCop
|
|
111
111
|
end
|
112
112
|
|
113
113
|
def requires_parens?(node)
|
114
|
-
(node.call_type? && node.arguments.any? && !node.parenthesized?) ||
|
115
|
-
node.or_type? || node.and_type?
|
114
|
+
(node.call_type? && node.arguments.any? && !node.parenthesized?) || node.operator_keyword?
|
116
115
|
end
|
117
116
|
|
118
117
|
def multi_argument(node)
|
@@ -155,8 +155,7 @@ module RuboCop
|
|
155
155
|
end
|
156
156
|
|
157
157
|
def require_parentheses?(condition)
|
158
|
-
condition.
|
159
|
-
(condition.send_type? && condition.comparison_method?)
|
158
|
+
condition.operator_keyword? || (condition.send_type? && condition.comparison_method?)
|
160
159
|
end
|
161
160
|
end
|
162
161
|
end
|
@@ -23,6 +23,7 @@ module RuboCop
|
|
23
23
|
|
24
24
|
def on_normal_if_unless(node)
|
25
25
|
return if node.parent&.if_type?
|
26
|
+
return if part_of_ignored_node?(node)
|
26
27
|
|
27
28
|
beginning = node.loc.begin
|
28
29
|
return unless beginning&.is?(';')
|
@@ -32,13 +33,14 @@ module RuboCop
|
|
32
33
|
add_offense(node, message: message) do |corrector|
|
33
34
|
autocorrect(corrector, node)
|
34
35
|
end
|
36
|
+
|
37
|
+
ignore_node(node)
|
35
38
|
end
|
36
39
|
|
37
40
|
private
|
38
41
|
|
39
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
40
42
|
def message(node)
|
41
|
-
template = if node
|
43
|
+
template = if require_newline?(node)
|
42
44
|
MSG_NEWLINE
|
43
45
|
elsif node.else_branch&.if_type? || node.else_branch&.begin_type? ||
|
44
46
|
use_block_in_branches?(node)
|
@@ -49,20 +51,27 @@ module RuboCop
|
|
49
51
|
|
50
52
|
format(template, expr: node.condition.source)
|
51
53
|
end
|
52
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
53
54
|
|
54
55
|
def autocorrect(corrector, node)
|
55
|
-
if
|
56
|
+
if require_newline?(node) || use_block_in_branches?(node)
|
56
57
|
corrector.replace(node.loc.begin, "\n")
|
57
58
|
else
|
58
59
|
corrector.replace(node, replacement(node))
|
59
60
|
end
|
60
61
|
end
|
61
62
|
|
63
|
+
def require_newline?(node)
|
64
|
+
node.branches.compact.any?(&:begin_type?) || use_return_with_argument?(node)
|
65
|
+
end
|
66
|
+
|
62
67
|
def use_block_in_branches?(node)
|
63
68
|
node.branches.compact.any? { |branch| branch.block_type? || branch.numblock_type? }
|
64
69
|
end
|
65
70
|
|
71
|
+
def use_return_with_argument?(node)
|
72
|
+
node.if_branch&.return_type? && node.if_branch&.arguments&.any?
|
73
|
+
end
|
74
|
+
|
66
75
|
def replacement(node)
|
67
76
|
return correct_elsif(node) if node.else_branch&.if_type?
|
68
77
|
|
@@ -111,7 +120,7 @@ module RuboCop
|
|
111
120
|
end
|
112
121
|
|
113
122
|
def require_argument_parentheses?(node)
|
114
|
-
return false
|
123
|
+
return false if !node.call_type? || node.arithmetic_operation?
|
115
124
|
|
116
125
|
!node.parenthesized? && node.arguments.any? && !node.method?(:[]) && !node.method?(:[]=)
|
117
126
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# When passing an existing hash as keyword arguments, provide additional arguments
|
7
|
+
# directly rather than using `merge`.
|
8
|
+
#
|
9
|
+
# Providing arguments directly is more performant than using `merge`, and
|
10
|
+
# also leads to shorter and simpler code.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# # bad
|
14
|
+
# some_method(**opts.merge(foo: true))
|
15
|
+
# some_method(**opts.merge(other_opts))
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# some_method(**opts, foo: true)
|
19
|
+
# some_method(**opts, **other_opts)
|
20
|
+
#
|
21
|
+
class KeywordArgumentsMerging < Base
|
22
|
+
extend AutoCorrector
|
23
|
+
|
24
|
+
MSG = 'Provide additional arguments directly rather than using `merge`.'
|
25
|
+
|
26
|
+
# @!method merge_kwargs?(node)
|
27
|
+
def_node_matcher :merge_kwargs?, <<~PATTERN
|
28
|
+
(send _ _
|
29
|
+
...
|
30
|
+
(hash
|
31
|
+
(kwsplat
|
32
|
+
$(send $_ :merge $...))
|
33
|
+
...))
|
34
|
+
PATTERN
|
35
|
+
|
36
|
+
def on_kwsplat(node)
|
37
|
+
return unless (ancestor = node.parent&.parent)
|
38
|
+
|
39
|
+
merge_kwargs?(ancestor) do |merge_node, hash_node, other_hash_node|
|
40
|
+
add_offense(merge_node) do |corrector|
|
41
|
+
autocorrect(corrector, node, hash_node, other_hash_node)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def autocorrect(corrector, kwsplat_node, hash_node, other_hash_node)
|
49
|
+
other_hash_node_replacement =
|
50
|
+
other_hash_node.map do |node|
|
51
|
+
if node.hash_type?
|
52
|
+
if node.braces?
|
53
|
+
node.source[1...-1]
|
54
|
+
else
|
55
|
+
node.source
|
56
|
+
end
|
57
|
+
else
|
58
|
+
"**#{node.source}"
|
59
|
+
end
|
60
|
+
end.join(', ')
|
61
|
+
|
62
|
+
corrector.replace(kwsplat_node, "**#{hash_node.source}, #{other_hash_node_replacement}")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -63,12 +63,17 @@ module RuboCop
|
|
63
63
|
|
64
64
|
MSG = 'Use `%<new_method_name>s` instead of `each` to map elements into an array.'
|
65
65
|
|
66
|
+
# @!method suitable_argument_node?(node)
|
67
|
+
def_node_matcher :suitable_argument_node?, <<-PATTERN
|
68
|
+
!{splat forwarded-restarg forwarded-args (hash (forwarded-kwrestarg)) (block-pass nil?)}
|
69
|
+
PATTERN
|
70
|
+
|
66
71
|
# @!method each_block_with_push?(node)
|
67
72
|
def_node_matcher :each_block_with_push?, <<-PATTERN
|
68
73
|
[
|
69
74
|
^({begin kwbegin block} ...)
|
70
75
|
({block numblock} (send !{nil? self} :each) _
|
71
|
-
(send (lvar _) {:<< :push :append}
|
76
|
+
(send (lvar _) {:<< :push :append} #suitable_argument_node?))
|
72
77
|
]
|
73
78
|
PATTERN
|
74
79
|
|
@@ -71,9 +71,11 @@ module RuboCop
|
|
71
71
|
return false if node.receiver
|
72
72
|
|
73
73
|
any_assignment?(node) do |asgn_node|
|
74
|
-
|
75
|
-
|
76
|
-
|
74
|
+
if asgn_node.masgn_type?
|
75
|
+
variable_in_mass_assignment?(node.method_name, asgn_node)
|
76
|
+
else
|
77
|
+
asgn_node.loc.name.source == node.method_name.to_s
|
78
|
+
end
|
77
79
|
end
|
78
80
|
end
|
79
81
|
|
@@ -98,20 +100,14 @@ module RuboCop
|
|
98
100
|
# `obj.method ||= value` parses as (or-asgn (send ...) ...)
|
99
101
|
# which IS an `asgn_node`. Similarly, `obj.method += value` parses
|
100
102
|
# as (op-asgn (send ...) ...), which is also an `asgn_node`.
|
101
|
-
if asgn_node.shorthand_asgn?
|
102
|
-
asgn_node, _value = *asgn_node
|
103
|
-
next if asgn_node.send_type?
|
104
|
-
end
|
103
|
+
next if asgn_node.shorthand_asgn? && asgn_node.lhs.send_type?
|
105
104
|
|
106
105
|
yield asgn_node
|
107
106
|
end
|
108
107
|
end
|
109
108
|
|
110
109
|
def variable_in_mass_assignment?(variable_name, node)
|
111
|
-
|
112
|
-
var_nodes = *mlhs_node
|
113
|
-
|
114
|
-
var_nodes.any? { |n| n.to_a.first == variable_name }
|
110
|
+
node.assignments.any? { |n| n.name == variable_name }
|
115
111
|
end
|
116
112
|
|
117
113
|
def offense_range(node)
|
@@ -6,19 +6,49 @@ module RuboCop
|
|
6
6
|
# Checks for the presence of `method_missing` without also
|
7
7
|
# defining `respond_to_missing?`.
|
8
8
|
#
|
9
|
+
# Not defining `respond_to_missing?` will cause metaprogramming
|
10
|
+
# methods like `respond_to?` to behave unexpectedly:
|
11
|
+
#
|
12
|
+
# [source,ruby]
|
13
|
+
# ----
|
14
|
+
# class StringDelegator
|
15
|
+
# def initialize(string)
|
16
|
+
# @string = string
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# def method_missing(name, *args)
|
20
|
+
# @string.send(name, *args)
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# delegator = StringDelegator.new("foo")
|
25
|
+
# # Claims to not respond to `upcase`.
|
26
|
+
# delegator.respond_to?(:upcase) # => false
|
27
|
+
# # But you can call it.
|
28
|
+
# delegator.upcase # => FOO
|
29
|
+
# ----
|
30
|
+
#
|
9
31
|
# @example
|
10
32
|
# # bad
|
11
33
|
# def method_missing(name, *args)
|
12
|
-
#
|
34
|
+
# if @delegate.respond_to?(name)
|
35
|
+
# @delegate.send(name, *args)
|
36
|
+
# else
|
37
|
+
# super
|
38
|
+
# end
|
13
39
|
# end
|
14
40
|
#
|
15
41
|
# # good
|
16
42
|
# def respond_to_missing?(name, include_private)
|
17
|
-
#
|
43
|
+
# @delegate.respond_to?(name) || super
|
18
44
|
# end
|
19
45
|
#
|
20
46
|
# def method_missing(name, *args)
|
21
|
-
#
|
47
|
+
# if @delegate.respond_to?(name)
|
48
|
+
# @delegate.send(name, *args)
|
49
|
+
# else
|
50
|
+
# super
|
51
|
+
# end
|
22
52
|
# end
|
23
53
|
#
|
24
54
|
class MissingRespondToMissing < Base
|
@@ -55,25 +55,19 @@ module RuboCop
|
|
55
55
|
MSG = 'Avoid comparing a variable with multiple items ' \
|
56
56
|
'in a conditional, use `Array#include?` instead.'
|
57
57
|
|
58
|
-
def on_new_investigation
|
59
|
-
reset_comparison
|
60
|
-
end
|
61
|
-
|
62
58
|
def on_or(node)
|
63
59
|
root_of_or_node = root_of_or_node(node)
|
64
|
-
|
65
60
|
return unless node == root_of_or_node
|
66
|
-
return unless
|
67
|
-
|
68
|
-
return
|
61
|
+
return unless nested_comparison?(node)
|
62
|
+
|
63
|
+
return unless (variable, values = find_offending_var(node))
|
64
|
+
return if values.size < comparisons_threshold
|
69
65
|
|
70
66
|
add_offense(node) do |corrector|
|
71
|
-
elements =
|
72
|
-
prefer_method = "[#{elements}].include?(#{
|
67
|
+
elements = values.map(&:source).join(', ')
|
68
|
+
prefer_method = "[#{elements}].include?(#{variable_name(variable)})"
|
73
69
|
|
74
70
|
corrector.replace(node, prefer_method)
|
75
|
-
|
76
|
-
reset_comparison
|
77
71
|
end
|
78
72
|
end
|
79
73
|
|
@@ -92,32 +86,25 @@ module RuboCop
|
|
92
86
|
(send $_ :== $lvar)
|
93
87
|
PATTERN
|
94
88
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
variables_in_node(node).count == 1
|
99
|
-
end
|
100
|
-
|
101
|
-
def variables_in_node(node)
|
89
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
90
|
+
def find_offending_var(node, variables = Set.new, values = [])
|
102
91
|
if node.or_type?
|
103
|
-
node.
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
92
|
+
find_offending_var(node.lhs, variables, values)
|
93
|
+
find_offending_var(node.rhs, variables, values)
|
94
|
+
elsif simple_double_comparison?(node)
|
95
|
+
return
|
96
|
+
elsif (var, obj = simple_comparison?(node))
|
97
|
+
return if allow_method_comparison? && obj.send_type?
|
108
98
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
if (var, obj = simple_comparison_lhs?(node)) || (obj, var = simple_comparison_rhs?(node))
|
114
|
-
@allowed_method_comparison = true if allow_method_comparison? && obj.send_type?
|
115
|
-
@compared_elements << obj.source
|
116
|
-
return [variable_name(var)]
|
99
|
+
variables << var
|
100
|
+
return if variables.size > 1
|
101
|
+
|
102
|
+
values << obj
|
117
103
|
end
|
118
104
|
|
119
|
-
[]
|
105
|
+
[variables.first, values] if variables.any?
|
120
106
|
end
|
107
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
121
108
|
|
122
109
|
def variable_name(node)
|
123
110
|
node.children[0]
|
@@ -132,7 +119,14 @@ module RuboCop
|
|
132
119
|
end
|
133
120
|
|
134
121
|
def comparison?(node)
|
135
|
-
|
122
|
+
simple_comparison?(node) || nested_comparison?(node)
|
123
|
+
end
|
124
|
+
|
125
|
+
def simple_comparison?(node)
|
126
|
+
if (var, obj = simple_comparison_lhs?(node)) ||
|
127
|
+
(obj, var = simple_comparison_rhs?(node))
|
128
|
+
[var, obj]
|
129
|
+
end
|
136
130
|
end
|
137
131
|
|
138
132
|
def root_of_or_node(or_node)
|
@@ -145,11 +139,6 @@ module RuboCop
|
|
145
139
|
end
|
146
140
|
end
|
147
141
|
|
148
|
-
def reset_comparison
|
149
|
-
@compared_elements = []
|
150
|
-
@allowed_method_comparison = false
|
151
|
-
end
|
152
|
-
|
153
142
|
def allow_method_comparison?
|
154
143
|
cop_config.fetch('AllowMethodComparison', true)
|
155
144
|
end
|
@@ -125,15 +125,14 @@ module RuboCop
|
|
125
125
|
MSG = 'Freeze mutable objects assigned to constants.'
|
126
126
|
|
127
127
|
def on_casgn(node)
|
128
|
-
|
129
|
-
if value.nil? # This is only the case for `CONST += ...` or similarg66
|
128
|
+
if node.expression.nil? # This is only the case for `CONST += ...` or similarg66
|
130
129
|
parent = node.parent
|
131
130
|
return unless parent.or_asgn_type? # We only care about `CONST ||= ...`
|
132
131
|
|
133
|
-
|
132
|
+
on_assignment(parent.children.last)
|
133
|
+
else
|
134
|
+
on_assignment(node.expression)
|
134
135
|
end
|
135
|
-
|
136
|
-
on_assignment(value)
|
137
136
|
end
|
138
137
|
|
139
138
|
private
|
@@ -46,10 +46,12 @@ module RuboCop
|
|
46
46
|
@corrected_nodes = nil
|
47
47
|
end
|
48
48
|
|
49
|
+
# rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity
|
49
50
|
def on_if(node)
|
50
51
|
return unless if_else?(node)
|
51
52
|
return unless (condition = unwrap_begin_nodes(node.condition))
|
52
53
|
return if double_negation?(condition) || !negated_condition?(condition)
|
54
|
+
return unless condition.arguments.size < 2
|
53
55
|
|
54
56
|
message = message(node)
|
55
57
|
add_offense(node, message: message) do |corrector|
|
@@ -62,6 +64,7 @@ module RuboCop
|
|
62
64
|
end
|
63
65
|
end
|
64
66
|
end
|
67
|
+
# rubocop:enable Metrics/AbcSize,Metrics/CyclomaticComplexity
|
65
68
|
|
66
69
|
private
|
67
70
|
|
@@ -92,13 +95,12 @@ module RuboCop
|
|
92
95
|
end
|
93
96
|
|
94
97
|
def correct_negated_condition(corrector, node)
|
95
|
-
receiver, method_name, rhs = *node
|
96
98
|
replacement =
|
97
99
|
if node.negation_method?
|
98
|
-
receiver.source
|
100
|
+
node.receiver.source
|
99
101
|
else
|
100
|
-
inverted_method = method_name.to_s.sub('!', '=')
|
101
|
-
"#{receiver.source} #{inverted_method} #{
|
102
|
+
inverted_method = node.method_name.to_s.sub('!', '=')
|
103
|
+
"#{node.receiver.source} #{inverted_method} #{node.first_argument.source}"
|
102
104
|
end
|
103
105
|
|
104
106
|
corrector.replace(node, replacement)
|
@@ -18,7 +18,6 @@ module RuboCop
|
|
18
18
|
class NestedTernaryOperator < Base
|
19
19
|
extend AutoCorrector
|
20
20
|
include RangeHelp
|
21
|
-
include IgnoredNode
|
22
21
|
|
23
22
|
MSG = 'Ternary operators must not be nested. Prefer `if` or `else` constructs instead.'
|
24
23
|
|
@@ -45,9 +44,11 @@ module RuboCop
|
|
45
44
|
end
|
46
45
|
|
47
46
|
def remove_parentheses(source)
|
48
|
-
|
49
|
-
|
50
|
-
|
47
|
+
if source.start_with?('(') && source.end_with?(')')
|
48
|
+
source.delete_prefix('(').delete_suffix(')')
|
49
|
+
else
|
50
|
+
source
|
51
|
+
end
|
51
52
|
end
|
52
53
|
|
53
54
|
def replace_loc_and_whitespace(corrector, range, replacement)
|