rubocop 1.75.5 → 1.77.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 +20 -14
- data/config/default.yml +74 -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/correctors/parentheses_corrector.rb +5 -2
- data/lib/rubocop/cop/gemspec/attribute_assignment.rb +91 -0
- data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +37 -15
- data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
- data/lib/rubocop/cop/gemspec/require_mfa.rb +15 -1
- data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +4 -4
- 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/closing_parenthesis_indentation.rb +1 -1
- 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/line_length.rb +26 -5
- data/lib/rubocop/cop/layout/space_before_brackets.rb +5 -38
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +8 -2
- data/lib/rubocop/cop/lint/ambiguous_range.rb +5 -0
- 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 +31 -4
- data/lib/rubocop/cop/lint/identity_comparison.rb +19 -15
- data/lib/rubocop/cop/lint/literal_as_condition.rb +19 -27
- data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +4 -4
- data/lib/rubocop/cop/lint/self_assignment.rb +25 -0
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +5 -0
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +29 -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/useless_ruby2_keywords.rb +3 -3
- data/lib/rubocop/cop/metrics/abc_size.rb +1 -1
- data/lib/rubocop/cop/mixin/alignment.rb +1 -1
- data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -1
- data/lib/rubocop/cop/mixin/gemspec_help.rb +22 -0
- data/lib/rubocop/cop/mixin/line_length_help.rb +24 -8
- data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
- data/lib/rubocop/cop/naming/file_name.rb +2 -2
- data/lib/rubocop/cop/naming/predicate_method.rb +281 -0
- data/lib/rubocop/cop/naming/{predicate_name.rb → predicate_prefix.rb} +4 -4
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +32 -10
- data/lib/rubocop/cop/style/case_like_if.rb +1 -1
- data/lib/rubocop/cop/style/collection_querying.rb +167 -0
- data/lib/rubocop/cop/style/command_literal.rb +1 -1
- data/lib/rubocop/cop/style/comparable_between.rb +3 -0
- 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/exponential_notation.rb +2 -2
- data/lib/rubocop/cop/style/fetch_env_var.rb +32 -6
- data/lib/rubocop/cop/style/hash_conversion.rb +12 -3
- data/lib/rubocop/cop/style/if_unless_modifier.rb +33 -6
- 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 +2 -2
- 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_interpolation.rb +1 -1
- data/lib/rubocop/cop/style/redundant_parentheses.rb +26 -5
- data/lib/rubocop/cop/style/redundant_self.rb +8 -5
- data/lib/rubocop/cop/style/regexp_literal.rb +1 -1
- data/lib/rubocop/cop/style/safe_navigation.rb +24 -11
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +6 -3
- 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/symbol_proc.rb +1 -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/fuubar_style_formatter.rb +1 -1
- data/lib/rubocop/formatter/html_formatter.rb +1 -1
- data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
- data/lib/rubocop/lsp/diagnostic.rb +4 -4
- data/lib/rubocop/rspec/expect_offense.rb +9 -3
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +8 -1
- data/lib/ruby_lsp/rubocop/addon.rb +2 -2
- metadata +15 -11
@@ -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
|
@@ -87,7 +87,7 @@ module RuboCop
|
|
87
87
|
true
|
88
88
|
end
|
89
89
|
|
90
|
-
def integral(node)
|
90
|
+
def integral?(node)
|
91
91
|
mantissa, = node.source.split('e')
|
92
92
|
/^-?[1-9](\d*[1-9])?$/.match?(mantissa)
|
93
93
|
end
|
@@ -101,7 +101,7 @@ module RuboCop
|
|
101
101
|
when :engineering
|
102
102
|
!engineering?(node)
|
103
103
|
when :integral
|
104
|
-
!integral(node)
|
104
|
+
!integral?(node)
|
105
105
|
else
|
106
106
|
false
|
107
107
|
end
|
@@ -9,7 +9,20 @@ module RuboCop
|
|
9
9
|
# On the other hand, `ENV.fetch` raises `KeyError` or returns the explicitly
|
10
10
|
# specified default value.
|
11
11
|
#
|
12
|
-
# @example
|
12
|
+
# @example DefaultToNil: true (default)
|
13
|
+
# # bad
|
14
|
+
# ENV['X']
|
15
|
+
# x = ENV['X']
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# ENV.fetch('X', nil)
|
19
|
+
# x = ENV.fetch('X', nil)
|
20
|
+
#
|
21
|
+
# # also good
|
22
|
+
# !ENV['X']
|
23
|
+
# ENV['X'].some_method # (e.g. `.nil?`)
|
24
|
+
#
|
25
|
+
# @example DefaultToNil: false
|
13
26
|
# # bad
|
14
27
|
# ENV['X']
|
15
28
|
# x = ENV['X']
|
@@ -25,7 +38,8 @@ module RuboCop
|
|
25
38
|
class FetchEnvVar < Base
|
26
39
|
extend AutoCorrector
|
27
40
|
|
28
|
-
|
41
|
+
MSG_WITH_NIL = 'Use `ENV.fetch(%<key>s, nil)` instead of `ENV[%<key>s]`.'
|
42
|
+
MSG_WITHOUT_NIL = 'Use `ENV.fetch(%<key>s)` instead of `ENV[%<key>s]`.'
|
29
43
|
RESTRICT_ON_SEND = [:[]].freeze
|
30
44
|
|
31
45
|
# @!method env_with_bracket?(node)
|
@@ -37,7 +51,7 @@ module RuboCop
|
|
37
51
|
env_with_bracket?(node) do |name_node|
|
38
52
|
break unless offensive?(node)
|
39
53
|
|
40
|
-
message = format(
|
54
|
+
message = format(offense_message, key: name_node.source)
|
41
55
|
add_offense(node, message: message) do |corrector|
|
42
56
|
corrector.replace(node, new_code(name_node))
|
43
57
|
end
|
@@ -46,6 +60,14 @@ module RuboCop
|
|
46
60
|
|
47
61
|
private
|
48
62
|
|
63
|
+
def default_to_nil?
|
64
|
+
cop_config.fetch('DefaultToNil', true)
|
65
|
+
end
|
66
|
+
|
67
|
+
def offense_message
|
68
|
+
default_to_nil? ? MSG_WITH_NIL : MSG_WITHOUT_NIL
|
69
|
+
end
|
70
|
+
|
49
71
|
def allowed_var?(node)
|
50
72
|
env_key_node = node.children.last
|
51
73
|
env_key_node.str_type? && cop_config['AllowedVars'].include?(env_key_node.value)
|
@@ -53,12 +75,12 @@ module RuboCop
|
|
53
75
|
|
54
76
|
def used_as_flag?(node)
|
55
77
|
return false if node.root?
|
56
|
-
return true if used_if_condition_in_body(node)
|
78
|
+
return true if used_if_condition_in_body?(node)
|
57
79
|
|
58
80
|
node.parent.send_type? && (node.parent.prefix_bang? || node.parent.comparison_method?)
|
59
81
|
end
|
60
82
|
|
61
|
-
def used_if_condition_in_body(node)
|
83
|
+
def used_if_condition_in_body?(node)
|
62
84
|
if_node = node.ancestors.find(&:if_type?)
|
63
85
|
|
64
86
|
return false unless (condition = if_node&.condition)
|
@@ -125,7 +147,11 @@ module RuboCop
|
|
125
147
|
end
|
126
148
|
|
127
149
|
def new_code(name_node)
|
128
|
-
|
150
|
+
if default_to_nil?
|
151
|
+
"ENV.fetch(#{name_node.source}, nil)"
|
152
|
+
else
|
153
|
+
"ENV.fetch(#{name_node.source})"
|
154
|
+
end
|
129
155
|
end
|
130
156
|
end
|
131
157
|
end
|
@@ -54,7 +54,7 @@ module RuboCop
|
|
54
54
|
def_node_matcher :hash_from_array?, '(send (const {nil? cbase} :Hash) :[] ...)'
|
55
55
|
|
56
56
|
def on_send(node)
|
57
|
-
return
|
57
|
+
return if part_of_ignored_node?(node) || !hash_from_array?(node)
|
58
58
|
|
59
59
|
# There are several cases:
|
60
60
|
# If there is one argument:
|
@@ -63,11 +63,12 @@ module RuboCop
|
|
63
63
|
# If there is 0 or 2+ arguments:
|
64
64
|
# Hash[a1, a2, a3, a4] => {a1 => a2, a3 => a4}
|
65
65
|
# ...but don't suggest correction if there is odd number of them (it is a bug)
|
66
|
-
node.arguments.
|
66
|
+
node.arguments.one? ? single_argument(node) : multi_argument(node)
|
67
67
|
end
|
68
68
|
|
69
69
|
private
|
70
70
|
|
71
|
+
# rubocop:disable Metrics/MethodLength
|
71
72
|
def single_argument(node)
|
72
73
|
first_argument = node.first_argument
|
73
74
|
if first_argument.hash_type?
|
@@ -82,8 +83,11 @@ module RuboCop
|
|
82
83
|
replacement = "(#{replacement})" if requires_parens?(first_argument)
|
83
84
|
corrector.replace(node, "#{replacement}.to_h")
|
84
85
|
end
|
86
|
+
|
87
|
+
ignore_node(node)
|
85
88
|
end
|
86
89
|
end
|
90
|
+
# rubocop:enable Metrics/MethodLength
|
87
91
|
|
88
92
|
def use_zip_method_without_argument?(first_argument)
|
89
93
|
return false unless first_argument&.send_type?
|
@@ -111,7 +115,12 @@ module RuboCop
|
|
111
115
|
end
|
112
116
|
|
113
117
|
def requires_parens?(node)
|
114
|
-
|
118
|
+
if node.call_type?
|
119
|
+
return false if node.method?(:[])
|
120
|
+
return true if node.arguments.any? && !node.parenthesized?
|
121
|
+
end
|
122
|
+
|
123
|
+
node.operator_keyword?
|
115
124
|
end
|
116
125
|
|
117
126
|
def multi_argument(node)
|
@@ -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
|
|
@@ -205,8 +223,17 @@ module RuboCop
|
|
205
223
|
|
206
224
|
def too_long_line_based_on_allow_uri?(line)
|
207
225
|
if allow_uri?
|
208
|
-
uri_range =
|
209
|
-
return false if uri_range &&
|
226
|
+
uri_range = find_excessive_range(line, :uri)
|
227
|
+
return false if uri_range && allowed_position?(line, uri_range)
|
228
|
+
end
|
229
|
+
|
230
|
+
true
|
231
|
+
end
|
232
|
+
|
233
|
+
def too_long_line_based_on_allow_qualified_name?(line)
|
234
|
+
if allow_qualified_name?
|
235
|
+
namespace_range = find_excessive_range(line, :namespace)
|
236
|
+
return false if namespace_range && allowed_position?(line, namespace_range)
|
210
237
|
end
|
211
238
|
|
212
239
|
true
|
@@ -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)
|
@@ -251,7 +251,7 @@ module RuboCop
|
|
251
251
|
return false unless (last_argument = node.last_argument)
|
252
252
|
return true if last_argument.forwarded_restarg_type?
|
253
253
|
|
254
|
-
last_argument.hash_type? && last_argument.children.
|
254
|
+
last_argument.hash_type? && last_argument.children.any?(&:forwarded_kwrestarg_type?)
|
255
255
|
end
|
256
256
|
end
|
257
257
|
# rubocop:enable Metrics/ModuleLength, Metrics/CyclomaticComplexity
|
@@ -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|
|