rubocop 1.75.3 → 1.76.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/README.md +20 -14
- data/config/default.yml +55 -7
- data/config/obsoletion.yml +6 -3
- data/lib/rubocop/cop/autocorrect_logic.rb +18 -10
- data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -1
- data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +50 -6
- data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +1 -0
- data/lib/rubocop/cop/layout/class_structure.rb +35 -0
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +7 -3
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/hash_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/leading_comment_space.rb +13 -1
- data/lib/rubocop/cop/layout/space_after_semicolon.rb +10 -0
- data/lib/rubocop/cop/layout/space_before_brackets.rb +6 -32
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +5 -1
- data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +3 -0
- data/lib/rubocop/cop/lint/ambiguous_range.rb +5 -0
- data/lib/rubocop/cop/lint/array_literal_in_regexp.rb +2 -3
- data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -1
- data/lib/rubocop/cop/lint/circular_argument_reference.rb +2 -5
- data/lib/rubocop/cop/lint/deprecated_class_methods.rb +1 -1
- data/lib/rubocop/cop/lint/duplicate_methods.rb +84 -2
- data/lib/rubocop/cop/lint/empty_interpolation.rb +3 -1
- data/lib/rubocop/cop/lint/float_comparison.rb +27 -0
- data/lib/rubocop/cop/lint/identity_comparison.rb +19 -15
- data/lib/rubocop/cop/lint/literal_as_condition.rb +31 -25
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +4 -4
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +5 -0
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +21 -4
- data/lib/rubocop/cop/lint/useless_assignment.rb +2 -0
- data/lib/rubocop/cop/lint/useless_default_value_argument.rb +90 -0
- data/lib/rubocop/cop/lint/useless_or.rb +98 -0
- data/lib/rubocop/cop/lint/void.rb +2 -2
- data/lib/rubocop/cop/metrics/abc_size.rb +1 -1
- data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -1
- data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +15 -14
- data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
- data/lib/rubocop/cop/mixin/trailing_comma.rb +6 -2
- data/lib/rubocop/cop/naming/predicate_method.rb +245 -0
- data/lib/rubocop/cop/naming/{predicate_name.rb → predicate_prefix.rb} +2 -2
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +32 -10
- data/lib/rubocop/cop/style/arguments_forwarding.rb +4 -1
- data/lib/rubocop/cop/style/class_and_module_children.rb +12 -2
- data/lib/rubocop/cop/style/command_literal.rb +1 -1
- data/lib/rubocop/cop/style/comparable_between.rb +5 -2
- data/lib/rubocop/cop/style/conditional_assignment.rb +3 -1
- data/lib/rubocop/cop/style/data_inheritance.rb +7 -0
- data/lib/rubocop/cop/style/def_with_parentheses.rb +18 -5
- data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +100 -0
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +3 -3
- data/lib/rubocop/cop/style/if_unless_modifier.rb +22 -4
- data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +4 -7
- data/lib/rubocop/cop/style/it_block_parameter.rb +33 -14
- data/lib/rubocop/cop/style/map_to_hash.rb +11 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +4 -1
- data/lib/rubocop/cop/style/min_max_comparison.rb +13 -5
- data/lib/rubocop/cop/style/multiline_if_modifier.rb +2 -0
- data/lib/rubocop/cop/style/percent_q_literals.rb +1 -1
- data/lib/rubocop/cop/style/redundant_array_flatten.rb +50 -0
- data/lib/rubocop/cop/style/redundant_format.rb +6 -1
- data/lib/rubocop/cop/style/redundant_parentheses.rb +31 -5
- data/lib/rubocop/cop/style/redundant_self.rb +5 -5
- data/lib/rubocop/cop/style/regexp_literal.rb +1 -1
- data/lib/rubocop/cop/style/safe_navigation.rb +25 -12
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +4 -2
- data/lib/rubocop/cop/style/string_concatenation.rb +1 -2
- data/lib/rubocop/cop/style/struct_inheritance.rb +8 -1
- data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +7 -1
- data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
- data/lib/rubocop/cop/team.rb +1 -1
- data/lib/rubocop/cop/variable_force/assignment.rb +7 -3
- data/lib/rubocop/formatter/disabled_config_formatter.rb +1 -0
- data/lib/rubocop/formatter/html_formatter.rb +1 -1
- data/lib/rubocop/rspec/cop_helper.rb +2 -2
- data/lib/rubocop/rspec/expect_offense.rb +9 -3
- data/lib/rubocop/rspec/shared_contexts.rb +1 -2
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +6 -1
- metadata +12 -7
@@ -25,8 +25,9 @@ module RuboCop
|
|
25
25
|
# # good
|
26
26
|
# def foo = do_something
|
27
27
|
#
|
28
|
-
# # good
|
28
|
+
# # good - without parentheses it's a syntax error
|
29
29
|
# def foo() do_something end
|
30
|
+
# def foo()=do_something
|
30
31
|
#
|
31
32
|
# # bad
|
32
33
|
# def Baz.foo()
|
@@ -38,19 +39,31 @@ module RuboCop
|
|
38
39
|
# do_something
|
39
40
|
# end
|
40
41
|
class DefWithParentheses < Base
|
42
|
+
include RangeHelp
|
41
43
|
extend AutoCorrector
|
42
44
|
|
43
45
|
MSG = "Omit the parentheses in defs when the method doesn't accept any arguments."
|
44
46
|
|
45
47
|
def on_def(node)
|
46
|
-
return
|
47
|
-
return
|
48
|
+
return unless !node.arguments? && (arguments_range = node.arguments.source_range)
|
49
|
+
return if parentheses_required?(node, arguments_range)
|
48
50
|
|
49
|
-
add_offense(
|
50
|
-
corrector.remove(
|
51
|
+
add_offense(arguments_range) do |corrector|
|
52
|
+
corrector.remove(arguments_range)
|
51
53
|
end
|
52
54
|
end
|
53
55
|
alias on_defs on_def
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def parentheses_required?(node, arguments_range)
|
60
|
+
return true if node.single_line? && !node.endless?
|
61
|
+
|
62
|
+
end_pos = arguments_range.end.end_pos
|
63
|
+
token_after_argument = range_between(end_pos, end_pos + 1).source
|
64
|
+
|
65
|
+
token_after_argument == '='
|
66
|
+
end
|
54
67
|
end
|
55
68
|
end
|
56
69
|
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Checks for empty strings being assigned inside string interpolation.
|
7
|
+
#
|
8
|
+
# Empty strings are a meaningless outcome inside of string interpolation, so we remove them.
|
9
|
+
# Alternatively, when configured to do so, we prioritise using empty strings.
|
10
|
+
#
|
11
|
+
# While this cop would also apply to variables that are only going to be used as strings,
|
12
|
+
# RuboCop can't detect that, so we only check inside of string interpolation.
|
13
|
+
#
|
14
|
+
# @example EnforcedStyle: trailing_conditional (default)
|
15
|
+
# # bad
|
16
|
+
# "#{condition ? 'foo' : ''}"
|
17
|
+
#
|
18
|
+
# # good
|
19
|
+
# "#{'foo' if condition}"
|
20
|
+
#
|
21
|
+
# # bad
|
22
|
+
# "#{condition ? '' : 'foo'}"
|
23
|
+
#
|
24
|
+
# # good
|
25
|
+
# "#{'foo' unless condition}"
|
26
|
+
#
|
27
|
+
# @example EnforcedStyle: ternary
|
28
|
+
# # bad
|
29
|
+
# "#{'foo' if condition}"
|
30
|
+
#
|
31
|
+
# # good
|
32
|
+
# "#{condition ? 'foo' : ''}"
|
33
|
+
#
|
34
|
+
# # bad
|
35
|
+
# "#{'foo' unless condition}"
|
36
|
+
#
|
37
|
+
# # good
|
38
|
+
# "#{condition ? '' : 'foo'}"
|
39
|
+
#
|
40
|
+
class EmptyStringInsideInterpolation < Base
|
41
|
+
include ConfigurableEnforcedStyle
|
42
|
+
include Interpolation
|
43
|
+
extend AutoCorrector
|
44
|
+
|
45
|
+
MSG_TRAILING_CONDITIONAL = 'Do not use trailing conditionals in string interpolation.'
|
46
|
+
MSG_TERNARY = 'Do not return empty strings in string interpolation.'
|
47
|
+
|
48
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
|
49
|
+
def on_interpolation(node)
|
50
|
+
node.each_child_node(:if) do |child_node|
|
51
|
+
if style == :trailing_conditional
|
52
|
+
if empty_if_outcome?(child_node)
|
53
|
+
ternary_style_autocorrect(child_node, child_node.else_branch.source, 'unless')
|
54
|
+
end
|
55
|
+
|
56
|
+
if empty_else_outcome?(child_node)
|
57
|
+
ternary_style_autocorrect(child_node, child_node.if_branch.source, 'if')
|
58
|
+
end
|
59
|
+
elsif style == :ternary
|
60
|
+
next unless child_node.modifier_form?
|
61
|
+
|
62
|
+
ternary_component = if child_node.unless?
|
63
|
+
"'' : #{child_node.if_branch.source}"
|
64
|
+
else
|
65
|
+
"#{child_node.if_branch.source} : ''"
|
66
|
+
end
|
67
|
+
|
68
|
+
add_offense(node, message: MSG_TRAILING_CONDITIONAL) do |corrector|
|
69
|
+
corrector.replace(node, "\#{#{child_node.condition.source} ? #{ternary_component}}")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def empty_if_outcome?(node)
|
79
|
+
empty_branch_outcome?(node.if_branch)
|
80
|
+
end
|
81
|
+
|
82
|
+
def empty_else_outcome?(node)
|
83
|
+
empty_branch_outcome?(node.else_branch)
|
84
|
+
end
|
85
|
+
|
86
|
+
def empty_branch_outcome?(branch)
|
87
|
+
return false unless branch
|
88
|
+
|
89
|
+
branch.nil_type? || (branch.str_type? && branch.value.empty?)
|
90
|
+
end
|
91
|
+
|
92
|
+
def ternary_style_autocorrect(node, outcome, condition)
|
93
|
+
add_offense(node, message: MSG_TERNARY) do |corrector|
|
94
|
+
corrector.replace(node, "#{outcome} #{condition} #{node.condition.source}")
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -189,7 +189,7 @@ module RuboCop
|
|
189
189
|
end
|
190
190
|
end
|
191
191
|
|
192
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
192
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
193
193
|
def check_expressions(node, expressions, insert_position)
|
194
194
|
return if expressions.any?(&:nil?)
|
195
195
|
|
@@ -197,7 +197,7 @@ module RuboCop
|
|
197
197
|
|
198
198
|
expressions.each do |expression|
|
199
199
|
add_offense(expression) do |corrector|
|
200
|
-
next if node.if_type? && node.ternary?
|
200
|
+
next if node.if_type? && (node.ternary? || node.then?)
|
201
201
|
|
202
202
|
range = range_by_whole_lines(expression.source_range, include_final_newline: true)
|
203
203
|
corrector.remove(range)
|
@@ -213,7 +213,7 @@ module RuboCop
|
|
213
213
|
end
|
214
214
|
end
|
215
215
|
end
|
216
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
216
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
217
217
|
|
218
218
|
def correct_assignment(corrector, node, expression, insert_position)
|
219
219
|
if insert_position == :after_condition
|
@@ -23,6 +23,17 @@ module RuboCop
|
|
23
23
|
# end
|
24
24
|
# ----
|
25
25
|
#
|
26
|
+
# The code `def method_name = body if condition` is considered a bad case by
|
27
|
+
# `Style/AmbiguousEndlessMethodDefinition` cop. So, to respect the user's intention to use
|
28
|
+
# an endless method definition in the `if` body, the following code is allowed:
|
29
|
+
#
|
30
|
+
# [source,ruby]
|
31
|
+
# ----
|
32
|
+
# if condition
|
33
|
+
# def method_name = body
|
34
|
+
# end
|
35
|
+
# ----
|
36
|
+
#
|
26
37
|
# NOTE: It is allowed when `defined?` argument has an undefined value,
|
27
38
|
# because using the modifier form causes the following incompatibility:
|
28
39
|
#
|
@@ -77,10 +88,14 @@ module RuboCop
|
|
77
88
|
[Style::SoleNestedConditional]
|
78
89
|
end
|
79
90
|
|
91
|
+
# rubocop:disable Metrics/AbcSize
|
80
92
|
def on_if(node)
|
93
|
+
return if endless_method?(node.body)
|
94
|
+
|
81
95
|
condition = node.condition
|
82
96
|
return if defined_nodes(condition).any? { |n| defined_argument_is_undefined?(node, n) } ||
|
83
97
|
pattern_matching_nodes(condition).any?
|
98
|
+
|
84
99
|
return unless (msg = message(node))
|
85
100
|
|
86
101
|
add_offense(node.loc.keyword, message: format(msg, keyword: node.keyword)) do |corrector|
|
@@ -90,9 +105,14 @@ module RuboCop
|
|
90
105
|
ignore_node(node)
|
91
106
|
end
|
92
107
|
end
|
108
|
+
# rubocop:enable Metrics/AbcSize
|
93
109
|
|
94
110
|
private
|
95
111
|
|
112
|
+
def endless_method?(body)
|
113
|
+
body&.any_def_type? && body.endless?
|
114
|
+
end
|
115
|
+
|
96
116
|
def defined_nodes(condition)
|
97
117
|
if condition.defined_type?
|
98
118
|
[condition]
|
@@ -112,12 +132,10 @@ module RuboCop
|
|
112
132
|
end
|
113
133
|
|
114
134
|
def pattern_matching_nodes(condition)
|
115
|
-
if condition.
|
135
|
+
if condition.any_match_pattern_type?
|
116
136
|
[condition]
|
117
137
|
else
|
118
|
-
condition.each_descendant.select
|
119
|
-
node.type?(:match_pattern, :match_pattern_p)
|
120
|
-
end
|
138
|
+
condition.each_descendant.select(&:any_match_pattern_type?)
|
121
139
|
end
|
122
140
|
end
|
123
141
|
|
@@ -28,19 +28,16 @@ module RuboCop
|
|
28
28
|
|
29
29
|
MSG = 'Avoid modifier `%<keyword>s` after another conditional.'
|
30
30
|
|
31
|
+
# rubocop:disable Metrics/AbcSize
|
31
32
|
def on_if(node)
|
32
33
|
return unless node.modifier_form? && node.body.if_type?
|
33
34
|
|
34
35
|
add_offense(node.loc.keyword, message: format(MSG, keyword: node.keyword)) do |corrector|
|
35
|
-
keyword
|
36
|
-
|
37
|
-
corrector.replace(node, <<~RUBY.chop)
|
38
|
-
#{keyword} #{node.condition.source}
|
39
|
-
#{node.if_branch.source}
|
40
|
-
end
|
41
|
-
RUBY
|
36
|
+
corrector.wrap(node.if_branch, "#{node.keyword} #{node.condition.source}\n", "\nend")
|
37
|
+
corrector.remove(node.if_branch.source_range.end.join(node.condition.source_range.end))
|
42
38
|
end
|
43
39
|
end
|
40
|
+
# rubocop:enable Metrics/AbcSize
|
44
41
|
end
|
45
42
|
end
|
46
43
|
end
|
@@ -5,15 +5,28 @@ module RuboCop
|
|
5
5
|
module Style
|
6
6
|
# Checks for blocks with one argument where `it` block parameter can be used.
|
7
7
|
#
|
8
|
-
# It provides
|
8
|
+
# It provides four `EnforcedStyle` options:
|
9
9
|
#
|
10
|
-
# 1. `
|
11
|
-
# 2. `
|
12
|
-
# 3. `
|
10
|
+
# 1. `allow_single_line` (default) ... Always uses the `it` block parameter in a single line.
|
11
|
+
# 2. `only_numbered_parameters` ... Detects only numbered block parameters.
|
12
|
+
# 3. `always` ... Always uses the `it` block parameter.
|
13
|
+
# 4. `disallow` ... Disallows the `it` block parameter.
|
13
14
|
#
|
14
|
-
# A single numbered parameter is detected when `
|
15
|
+
# A single numbered parameter is detected when `allow_single_line`,
|
16
|
+
# `only_numbered_parameters`, or `always`.
|
15
17
|
#
|
16
|
-
# @example EnforcedStyle:
|
18
|
+
# @example EnforcedStyle: allow_single_line (default)
|
19
|
+
# # bad
|
20
|
+
# block do
|
21
|
+
# do_something(it)
|
22
|
+
# end
|
23
|
+
# block { do_something(_1) }
|
24
|
+
#
|
25
|
+
# # good
|
26
|
+
# block { do_something(it) }
|
27
|
+
# block { |named_param| do_something(named_param) }
|
28
|
+
#
|
29
|
+
# @example EnforcedStyle: only_numbered_parameters
|
17
30
|
# # bad
|
18
31
|
# block { do_something(_1) }
|
19
32
|
#
|
@@ -42,8 +55,9 @@ module RuboCop
|
|
42
55
|
extend TargetRubyVersion
|
43
56
|
extend AutoCorrector
|
44
57
|
|
45
|
-
|
46
|
-
|
58
|
+
MSG_USE_IT_PARAMETER = 'Use `it` block parameter.'
|
59
|
+
MSG_AVOID_IT_PARAMETER = 'Avoid using `it` block parameter.'
|
60
|
+
MSG_AVOID_IT_PARAMETER_MULTILINE = 'Avoid using `it` block parameter for multi-line blocks.'
|
47
61
|
|
48
62
|
minimum_target_ruby_version 3.4
|
49
63
|
|
@@ -57,7 +71,7 @@ module RuboCop
|
|
57
71
|
variables = find_block_variables(node, node.first_argument.source)
|
58
72
|
|
59
73
|
variables.each do |variable|
|
60
|
-
add_offense(variable, message:
|
74
|
+
add_offense(variable, message: MSG_USE_IT_PARAMETER) do |corrector|
|
61
75
|
corrector.remove(node.arguments)
|
62
76
|
corrector.replace(variable, 'it')
|
63
77
|
end
|
@@ -71,19 +85,24 @@ module RuboCop
|
|
71
85
|
variables = find_block_variables(node, '_1')
|
72
86
|
|
73
87
|
variables.each do |variable|
|
74
|
-
add_offense(variable, message:
|
88
|
+
add_offense(variable, message: MSG_USE_IT_PARAMETER) do |corrector|
|
75
89
|
corrector.replace(variable, 'it')
|
76
90
|
end
|
77
91
|
end
|
78
92
|
end
|
79
93
|
|
80
94
|
def on_itblock(node)
|
81
|
-
|
95
|
+
case style
|
96
|
+
when :allow_single_line
|
97
|
+
return if node.single_line?
|
82
98
|
|
83
|
-
|
99
|
+
add_offense(node, message: MSG_AVOID_IT_PARAMETER_MULTILINE)
|
100
|
+
when :disallow
|
101
|
+
variables = find_block_variables(node, 'it')
|
84
102
|
|
85
|
-
|
86
|
-
|
103
|
+
variables.each do |variable|
|
104
|
+
add_offense(variable, message: MSG_AVOID_IT_PARAMETER)
|
105
|
+
end
|
87
106
|
end
|
88
107
|
end
|
89
108
|
|
@@ -45,6 +45,11 @@ module RuboCop
|
|
45
45
|
}
|
46
46
|
PATTERN
|
47
47
|
|
48
|
+
# @!method destructuring_argument(node)
|
49
|
+
def_node_matcher :destructuring_argument, <<~PATTERN
|
50
|
+
(args $(mlhs (arg _)+))
|
51
|
+
PATTERN
|
52
|
+
|
48
53
|
def self.autocorrect_incompatible_with
|
49
54
|
[Layout::SingleLineBlockChain]
|
50
55
|
end
|
@@ -73,6 +78,12 @@ module RuboCop
|
|
73
78
|
corrector.replace(map_dot, to_h.loc.dot.source)
|
74
79
|
end
|
75
80
|
corrector.replace(map.loc.selector, 'to_h')
|
81
|
+
|
82
|
+
return unless map.parent.block_type?
|
83
|
+
|
84
|
+
if (argument = destructuring_argument(map.parent.arguments))
|
85
|
+
corrector.replace(argument, argument.source[1..-2])
|
86
|
+
end
|
76
87
|
end
|
77
88
|
# rubocop:enable Metrics/AbcSize
|
78
89
|
end
|
@@ -167,7 +167,7 @@ module RuboCop
|
|
167
167
|
def call_in_match_pattern?(node)
|
168
168
|
return false unless (parent = node.parent)
|
169
169
|
|
170
|
-
parent.
|
170
|
+
parent.any_match_pattern_type?
|
171
171
|
end
|
172
172
|
|
173
173
|
def hash_literal_in_arguments?(node)
|
@@ -222,6 +222,9 @@ module RuboCop
|
|
222
222
|
end
|
223
223
|
|
224
224
|
def unary_literal?(node)
|
225
|
+
# NOTE: should be removed after releasing https://github.com/rubocop/rubocop-ast/pull/379
|
226
|
+
return node.source.match?(/\A[+-]/) if node.complex_type?
|
227
|
+
|
225
228
|
(node.numeric_type? && node.sign?) ||
|
226
229
|
(node.parent&.send_type? && node.parent.unary_operation?)
|
227
230
|
end
|
@@ -39,13 +39,21 @@ module RuboCop
|
|
39
39
|
include RangeHelp
|
40
40
|
|
41
41
|
MSG = 'Use `%<prefer>s` instead.'
|
42
|
-
|
42
|
+
GREATER_OPERATORS = %i[> >=].freeze
|
43
43
|
LESS_OPERATORS = %i[< <=].freeze
|
44
|
-
COMPARISON_OPERATORS =
|
44
|
+
COMPARISON_OPERATORS = (GREATER_OPERATORS + LESS_OPERATORS).to_set.freeze
|
45
|
+
|
46
|
+
# @!method comparison_condition(node, name)
|
47
|
+
def_node_matcher :comparison_condition, <<~PATTERN
|
48
|
+
{
|
49
|
+
(send $_lhs $COMPARISON_OPERATORS $_rhs)
|
50
|
+
(begin (send $_lhs $COMPARISON_OPERATORS $_rhs))
|
51
|
+
}
|
52
|
+
PATTERN
|
45
53
|
|
46
54
|
def on_if(node)
|
47
|
-
lhs, operator, rhs =
|
48
|
-
return unless
|
55
|
+
lhs, operator, rhs = comparison_condition(node.condition)
|
56
|
+
return unless operator
|
49
57
|
|
50
58
|
if_branch = node.if_branch
|
51
59
|
else_branch = node.else_branch
|
@@ -63,7 +71,7 @@ module RuboCop
|
|
63
71
|
|
64
72
|
def preferred_method(operator, lhs, rhs, if_branch, else_branch)
|
65
73
|
if lhs == if_branch && rhs == else_branch
|
66
|
-
|
74
|
+
GREATER_OPERATORS.include?(operator) ? 'max' : 'min'
|
67
75
|
elsif lhs == else_branch && rhs == if_branch
|
68
76
|
LESS_OPERATORS.include?(operator) ? 'max' : 'min'
|
69
77
|
end
|
@@ -23,11 +23,13 @@ module RuboCop
|
|
23
23
|
'clause in a multiline statement.'
|
24
24
|
|
25
25
|
def on_if(node)
|
26
|
+
return if part_of_ignored_node?(node)
|
26
27
|
return unless node.modifier_form? && node.body.multiline?
|
27
28
|
|
28
29
|
add_offense(node, message: format(MSG, keyword: node.keyword)) do |corrector|
|
29
30
|
corrector.replace(node, to_normal_if(node))
|
30
31
|
end
|
32
|
+
ignore_node(node)
|
31
33
|
end
|
32
34
|
|
33
35
|
private
|
@@ -45,7 +45,7 @@ module RuboCop
|
|
45
45
|
# Report offense only if changing case doesn't change semantics,
|
46
46
|
# i.e., if the string would become dynamic or has special characters.
|
47
47
|
ast = parse(corrected(node.source)).ast
|
48
|
-
return if node.children != ast
|
48
|
+
return if node.children != ast&.children
|
49
49
|
|
50
50
|
add_offense(node.loc.begin) do |corrector|
|
51
51
|
corrector.replace(node, corrected(node.source))
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Checks for redundant calls of `Array#flatten`.
|
7
|
+
#
|
8
|
+
# `Array#join` joins nested arrays recursively, so flattening an array
|
9
|
+
# beforehand is redundant.
|
10
|
+
#
|
11
|
+
# @safety
|
12
|
+
# Cop is unsafe because the receiver of `flatten` method might not
|
13
|
+
# be an `Array`, so it's possible it won't respond to `join` method,
|
14
|
+
# or the end result would be different.
|
15
|
+
# Also, if the global variable `$,` is set to a value other than the default `nil`,
|
16
|
+
# false positives may occur.
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
# # bad
|
20
|
+
# x.flatten.join
|
21
|
+
# x.flatten(1).join
|
22
|
+
#
|
23
|
+
# # good
|
24
|
+
# x.join
|
25
|
+
#
|
26
|
+
class RedundantArrayFlatten < Base
|
27
|
+
extend AutoCorrector
|
28
|
+
|
29
|
+
MSG = 'Remove the redundant `flatten`.'
|
30
|
+
|
31
|
+
RESTRICT_ON_SEND = %i[flatten].freeze
|
32
|
+
|
33
|
+
# @!method flatten_join?(node)
|
34
|
+
def_node_matcher :flatten_join?, <<~PATTERN
|
35
|
+
(call (call !nil? :flatten _?) :join (nil)?)
|
36
|
+
PATTERN
|
37
|
+
|
38
|
+
def on_send(node)
|
39
|
+
return unless flatten_join?(node.parent)
|
40
|
+
|
41
|
+
range = node.loc.dot.begin.join(node.source_range.end)
|
42
|
+
add_offense(range) do |corrector|
|
43
|
+
corrector.remove(range)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
alias on_csend on_send
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -121,7 +121,12 @@ module RuboCop
|
|
121
121
|
def register_all_fields_literal(node, string, arguments)
|
122
122
|
return unless all_fields_literal?(string, arguments.dup)
|
123
123
|
|
124
|
-
|
124
|
+
format_arguments = argument_values(arguments)
|
125
|
+
begin
|
126
|
+
formatted_string = format(string, *format_arguments)
|
127
|
+
rescue ArgumentError
|
128
|
+
return
|
129
|
+
end
|
125
130
|
replacement = quote(formatted_string, node)
|
126
131
|
|
127
132
|
add_offense(node, message: message(node, replacement)) do |corrector|
|
@@ -49,6 +49,7 @@ module RuboCop
|
|
49
49
|
empty_parentheses?(node) ||
|
50
50
|
first_arg_begins_with_hash_literal?(node) ||
|
51
51
|
rescue?(node) ||
|
52
|
+
in_pattern_matching_in_method_argument?(node) ||
|
52
53
|
allowed_pin_operator?(node) ||
|
53
54
|
allowed_expression?(node)
|
54
55
|
end
|
@@ -122,6 +123,13 @@ module RuboCop
|
|
122
123
|
hash_literal && first_argument?(node) && !parentheses?(hash_literal) && !parenthesized
|
123
124
|
end
|
124
125
|
|
126
|
+
def in_pattern_matching_in_method_argument?(begin_node)
|
127
|
+
return false unless begin_node.parent&.call_type?
|
128
|
+
return false unless (node = begin_node.children.first)
|
129
|
+
|
130
|
+
target_ruby_version <= 2.7 ? node.match_pattern_type? : node.match_pattern_p_type?
|
131
|
+
end
|
132
|
+
|
125
133
|
def method_chain_begins_with_hash_literal(node)
|
126
134
|
return if node.nil?
|
127
135
|
return node if node.hash_type?
|
@@ -134,7 +142,7 @@ module RuboCop
|
|
134
142
|
node = begin_node.children.first
|
135
143
|
|
136
144
|
if (message = find_offense_message(begin_node, node))
|
137
|
-
if node.range_type? && !argument_of_parenthesized_method_call?(begin_node)
|
145
|
+
if node.range_type? && !argument_of_parenthesized_method_call?(begin_node, node)
|
138
146
|
begin_node = begin_node.parent
|
139
147
|
end
|
140
148
|
|
@@ -156,8 +164,11 @@ module RuboCop
|
|
156
164
|
if node.lambda_or_proc? && (node.braces? || node.send_node.lambda_literal?)
|
157
165
|
return 'an expression'
|
158
166
|
end
|
167
|
+
if disallowed_one_line_pattern_matching?(begin_node, node)
|
168
|
+
return 'a one-line pattern matching'
|
169
|
+
end
|
159
170
|
return 'an interpolated expression' if interpolation?(begin_node)
|
160
|
-
return 'a method argument' if argument_of_parenthesized_method_call?(begin_node)
|
171
|
+
return 'a method argument' if argument_of_parenthesized_method_call?(begin_node, node)
|
161
172
|
|
162
173
|
return if begin_node.chained?
|
163
174
|
|
@@ -180,10 +191,19 @@ module RuboCop
|
|
180
191
|
# @!method interpolation?(node)
|
181
192
|
def_node_matcher :interpolation?, '[^begin ^^dstr]'
|
182
193
|
|
183
|
-
def argument_of_parenthesized_method_call?(node)
|
184
|
-
|
194
|
+
def argument_of_parenthesized_method_call?(begin_node, node)
|
195
|
+
if node.basic_conditional? || node.rescue_type? || method_call_parentheses_required?(node)
|
196
|
+
return false
|
197
|
+
end
|
198
|
+
return false unless (parent = begin_node.parent)
|
185
199
|
|
186
|
-
parent.call_type? && parent.parenthesized? && parent.receiver !=
|
200
|
+
parent.call_type? && parent.parenthesized? && parent.receiver != begin_node
|
201
|
+
end
|
202
|
+
|
203
|
+
def method_call_parentheses_required?(node)
|
204
|
+
return false unless node.call_type?
|
205
|
+
|
206
|
+
(node.receiver.nil? || node.loc.dot) && node.arguments.any?
|
187
207
|
end
|
188
208
|
|
189
209
|
def allow_in_multiline_conditions?
|
@@ -234,6 +254,12 @@ module RuboCop
|
|
234
254
|
end
|
235
255
|
end
|
236
256
|
|
257
|
+
def disallowed_one_line_pattern_matching?(begin_node, node)
|
258
|
+
return false if begin_node.parent&.any_def_type? && begin_node.parent.endless?
|
259
|
+
|
260
|
+
node.any_match_pattern_type? && node.each_ancestor.none?(&:operator_keyword?)
|
261
|
+
end
|
262
|
+
|
237
263
|
def raised_to_power_negative_numeric?(begin_node, node)
|
238
264
|
return false unless node.numeric_type?
|
239
265
|
|
@@ -123,11 +123,11 @@ module RuboCop
|
|
123
123
|
def on_if(node)
|
124
124
|
# Allow conditional nodes to use `self` in the condition if that variable
|
125
125
|
# name is used in an `lvasgn` or `masgn` within the `if`.
|
126
|
-
node.
|
127
|
-
if
|
128
|
-
add_lhs_to_local_variables_scopes(node.condition,
|
129
|
-
|
130
|
-
add_masgn_lhs_variables(node.condition,
|
126
|
+
node.each_descendant(:lvasgn, :masgn) do |descendant_node|
|
127
|
+
if descendant_node.lvasgn_type?
|
128
|
+
add_lhs_to_local_variables_scopes(node.condition, descendant_node.lhs)
|
129
|
+
else
|
130
|
+
add_masgn_lhs_variables(node.condition, descendant_node.lhs)
|
131
131
|
end
|
132
132
|
end
|
133
133
|
end
|
@@ -155,7 +155,7 @@ module RuboCop
|
|
155
155
|
end
|
156
156
|
|
157
157
|
def preferred_delimiters
|
158
|
-
config.for_cop('Style/PercentLiteralDelimiters')
|
158
|
+
config.for_cop('Style/PercentLiteralDelimiters')['PreferredDelimiters']['%r'].chars
|
159
159
|
end
|
160
160
|
|
161
161
|
def allowed_omit_parentheses_with_percent_r_literal?(node)
|