rubocop 1.56.4 → 1.57.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +11 -0
  4. data/lib/rubocop/cli.rb +1 -1
  5. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  6. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +16 -1
  7. data/lib/rubocop/cop/layout/space_inside_parens.rb +1 -1
  8. data/lib/rubocop/cop/lint/debugger.rb +10 -1
  9. data/lib/rubocop/cop/lint/empty_block.rb +1 -1
  10. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
  11. data/lib/rubocop/cop/lint/mixed_case_range.rb +1 -1
  12. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +0 -1
  13. data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -0
  14. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +20 -4
  15. data/lib/rubocop/cop/metrics/block_length.rb +1 -1
  16. data/lib/rubocop/cop/metrics/class_length.rb +2 -2
  17. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +2 -2
  18. data/lib/rubocop/cop/style/class_equality_comparison.rb +5 -0
  19. data/lib/rubocop/cop/style/format_string.rb +24 -3
  20. data/lib/rubocop/cop/style/guard_clause.rb +26 -0
  21. data/lib/rubocop/cop/style/identical_conditional_branches.rb +17 -3
  22. data/lib/rubocop/cop/style/multiline_block_chain.rb +1 -1
  23. data/lib/rubocop/cop/style/nested_ternary_operator.rb +3 -11
  24. data/lib/rubocop/cop/style/redundant_begin.rb +9 -1
  25. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +86 -5
  26. data/lib/rubocop/cop/style/redundant_exception.rb +32 -12
  27. data/lib/rubocop/cop/style/redundant_filter_chain.rb +18 -2
  28. data/lib/rubocop/cop/style/redundant_parentheses.rb +21 -5
  29. data/lib/rubocop/cop/style/single_line_do_end_block.rb +65 -0
  30. data/lib/rubocop/server/cache.rb +1 -0
  31. data/lib/rubocop/version.rb +1 -1
  32. data/lib/rubocop.rb +1 -0
  33. metadata +10 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f3088047e83f70bb0665d28875e2498dce9f6c9066be1524691b3205b155953e
4
- data.tar.gz: 37aa8682af6da472cb469924c7a5407b0b947f15a519356dc035f45e4e82982d
3
+ metadata.gz: d38c0bf9376bd86de4bdf5561b74b11e895bc90ac263a8c29520afec2fb7d0b0
4
+ data.tar.gz: 5e624dd813953b2058306d07986c19d55fa170fa8f3960844c4d62f2f87b3277
5
5
  SHA512:
6
- metadata.gz: d075e0720342cb33925fbbb41d13ddbfe6d060dd92df26890b839e7fb3a65ea6b78e0f0cc42ed31fb038e363e2ec57c5e068e8372ed2e39f48cba2c922bc8e9a
7
- data.tar.gz: 7807c06a028201f01c168298f3587160d7d84d7eb8d968b01ecf33772180b8bdeba5dd1fd6a38b60ed54bb259fc306eb46d174baee5f66222986d467b38e2a85
6
+ metadata.gz: dee188b376a118be4c08956361312fc91bf4e55cdadfa8129611f8a3b19fef80fb8241658778a5f6fb042336ba4137c6c64d2a65189ad00567730934e3dcde74
7
+ data.tar.gz: 4dbf33a643a4f945d3564426b49338d463a55e734812d8c3113df04aa430577c2142b7685990bdd12915e537a1add4b181dd96488632dd89a544a33c5f712944
data/README.md CHANGED
@@ -53,7 +53,7 @@ To prevent an unwanted RuboCop update you might want to use a conservative versi
53
53
  in your `Gemfile`:
54
54
 
55
55
  ```rb
56
- gem 'rubocop', '~> 1.56', require: false
56
+ gem 'rubocop', '~> 1.57', require: false
57
57
  ```
58
58
 
59
59
  See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) for further details.
data/config/default.yml CHANGED
@@ -2170,7 +2170,9 @@ Lint/RedundantRegexpQuantifiers:
2170
2170
  Lint/RedundantRequireStatement:
2171
2171
  Description: 'Checks for unnecessary `require` statement.'
2172
2172
  Enabled: true
2173
+ SafeAutoCorrect: false
2173
2174
  VersionAdded: '0.76'
2175
+ VersionChanged: '1.57'
2174
2176
 
2175
2177
  Lint/RedundantSafeNavigation:
2176
2178
  Description: 'Checks for redundant safe navigation calls.'
@@ -3354,7 +3356,9 @@ Style/ClassEqualityComparison:
3354
3356
  Description: 'Enforces the use of `Object#instance_of?` instead of class comparison for equality.'
3355
3357
  StyleGuide: '#instance-of-vs-class-comparison'
3356
3358
  Enabled: true
3359
+ SafeAutoCorrect: false
3357
3360
  VersionAdded: '0.93'
3361
+ VersionChanged: '1.57'
3358
3362
  AllowedMethods:
3359
3363
  - ==
3360
3364
  - equal?
@@ -4920,7 +4924,9 @@ Style/RedundantFilterChain:
4920
4924
  Identifies usages of `any?`, `empty?`, `none?` or `one?` predicate methods chained to
4921
4925
  `select`/`filter`/`find_all` and change them to use predicate method instead.
4922
4926
  Enabled: pending
4927
+ SafeAutoCorrect: false
4923
4928
  VersionAdded: '1.52'
4929
+ VersionChanged: '1.57'
4924
4930
 
4925
4931
  Style/RedundantFreeze:
4926
4932
  Description: "Checks usages of Object#freeze on immutable objects."
@@ -5182,6 +5188,11 @@ Style/SingleLineBlockParams:
5182
5188
  - acc
5183
5189
  - elem
5184
5190
 
5191
+ Style/SingleLineDoEndBlock:
5192
+ Description: 'Checks for single-line `do`...`end` blocks.'
5193
+ Enabled: pending
5194
+ VersionAdded: '1.57'
5195
+
5185
5196
  Style/SingleLineMethods:
5186
5197
  Description: 'Avoid single-line methods.'
5187
5198
  StyleGuide: '#no-single-line-methods'
data/lib/rubocop/cli.rb CHANGED
@@ -11,7 +11,7 @@ module RuboCop
11
11
  STATUS_ERROR = 2
12
12
  STATUS_INTERRUPTED = Signal.list['INT'] + 128
13
13
  DEFAULT_PARALLEL_OPTIONS = %i[
14
- color debug display_style_guide display_time display_only_fail_level_offenses
14
+ color config debug display_style_guide display_time display_only_fail_level_offenses
15
15
  display_only_failed except extra_details fail_level fix_layout format
16
16
  ignore_disable_comments lint only only_guide_cops require safe
17
17
  autocorrect safe_autocorrect autocorrect_all
@@ -354,7 +354,7 @@ module RuboCop
354
354
  # Don't check indentation if the line doesn't start with the body.
355
355
  # For example, lines like "else do_something".
356
356
  first_char_pos_on_line = body_node.source_range.source_line =~ /\S/
357
- true unless body_node.loc.column == first_char_pos_on_line
357
+ body_node.loc.column != first_char_pos_on_line
358
358
  end
359
359
 
360
360
  def offending_range(body_node, indentation)
@@ -182,7 +182,7 @@ module RuboCop
182
182
  return unless rhs.source.start_with?('.', '&.')
183
183
 
184
184
  node = semantic_alignment_node(node)
185
- return unless node&.loc&.selector
185
+ return unless node&.loc&.selector && node.loc.dot
186
186
 
187
187
  node.loc.dot.join(node.loc.selector)
188
188
  end
@@ -204,6 +204,10 @@ module RuboCop
204
204
  dot_right_above = get_dot_right_above(node)
205
205
  return dot_right_above if dot_right_above
206
206
 
207
+ if (multiline_block_chain_node = find_multiline_block_chain_node(node))
208
+ return multiline_block_chain_node
209
+ end
210
+
207
211
  node = first_call_has_a_dot(node)
208
212
  return if node.loc.dot.line != node.first_line
209
213
 
@@ -219,6 +223,17 @@ module RuboCop
219
223
  end
220
224
  end
221
225
 
226
+ def find_multiline_block_chain_node(node)
227
+ return unless (block_node = node.each_descendant(:block, :numblock).first)
228
+ return unless block_node.multiline? && block_node.parent.call_type?
229
+
230
+ if node.receiver.call_type?
231
+ node.receiver
232
+ else
233
+ block_node.parent
234
+ end
235
+ end
236
+
222
237
  def first_call_has_a_dot(node)
223
238
  # descend to root of method chain
224
239
  node = node.receiver while node.receiver
@@ -168,7 +168,7 @@ module RuboCop
168
168
  # follows, and that the rules for space inside don't apply.
169
169
  return true if token2.comment?
170
170
 
171
- true unless same_line?(token1, token2) && !token1.space_after?
171
+ !same_line?(token1, token2) || token1.space_after?
172
172
  end
173
173
  end
174
174
  end
@@ -95,8 +95,11 @@ module RuboCop
95
95
  def assumed_usage_context?(node)
96
96
  # Basically, debugger methods are not used as a method argument without arguments.
97
97
  return false unless node.arguments.empty? && node.each_ancestor(:send, :csend).any?
98
+ return true if assumed_argument?(node)
98
99
 
99
- node.each_ancestor.none?(&:lambda_or_proc?)
100
+ node.each_ancestor.none? do |ancestor|
101
+ ancestor.block_type? || ancestor.numblock_type? || ancestor.lambda_or_proc?
102
+ end
100
103
  end
101
104
 
102
105
  def chained_method_name(send_node)
@@ -109,6 +112,12 @@ module RuboCop
109
112
  end
110
113
  chained_method_name
111
114
  end
115
+
116
+ def assumed_argument?(node)
117
+ parent = node.parent
118
+
119
+ parent.call_type? || parent.literal? || parent.pair_type?
120
+ end
112
121
  end
113
122
  end
114
123
  end
@@ -5,7 +5,7 @@ module RuboCop
5
5
  module Lint
6
6
  # Checks for blocks without a body.
7
7
  # Such empty blocks are typically an oversight or we should provide a comment
8
- # be clearer what we're aiming for.
8
+ # to clarify what we're aiming for.
9
9
  #
10
10
  # Empty lambdas and procs are ignored by default.
11
11
  #
@@ -34,7 +34,7 @@ module RuboCop
34
34
  # interpolation should not be removed if the expanded value
35
35
  # contains a space character.
36
36
  expanded_value = autocorrected_value(final_node)
37
- return if in_array_percent_literal?(begin_node) && /\s/.match?(expanded_value)
37
+ return if in_array_percent_literal?(begin_node) && /\s|\A\z/.match?(expanded_value)
38
38
 
39
39
  add_offense(final_node) do |corrector|
40
40
  return if final_node.dstr_type? # nested, fixed in next iteration
@@ -95,7 +95,7 @@ module RuboCop
95
95
 
96
96
  def skip_range?(range_start, range_end)
97
97
  [range_start, range_end].any? do |bound|
98
- bound.type == :escape
98
+ bound.type != :literal
99
99
  end
100
100
  end
101
101
 
@@ -43,7 +43,6 @@ module RuboCop
43
43
  #
44
44
  class NonAtomicFileOperation < Base
45
45
  extend AutoCorrector
46
- include Alignment
47
46
 
48
47
  MSG_REMOVE_FILE_EXIST_CHECK = 'Remove unnecessary existence check ' \
49
48
  '`%<receiver>s.%<method_name>s`.'
@@ -24,6 +24,10 @@ module RuboCop
24
24
  #
25
25
  # This cop target those features.
26
26
  #
27
+ # @safety
28
+ # This cop's autocorrection is unsafe because if `require 'pp'` is removed from one file,
29
+ # `NameError` can be encountered when another file uses `PP.pp`.
30
+ #
27
31
  # @example
28
32
  # # bad
29
33
  # require 'unloaded_feature'
@@ -4,8 +4,12 @@ module RuboCop
4
4
  module Cop
5
5
  module Lint
6
6
  # Checks for redundant safe navigation calls.
7
- # `instance_of?`, `kind_of?`, `is_a?`, `eql?`, `respond_to?`, and `equal?` methods
8
- # are checked by default. These are customizable with `AllowedMethods` option.
7
+ # Use cases where a constant, named in camel case for classes and modules is `nil` are rare,
8
+ # and an offense is not detected when the receiver is a snake case constant.
9
+ #
10
+ # For all receivers, the `instance_of?`, `kind_of?`, `is_a?`, `eql?`, `respond_to?`,
11
+ # and `equal?` methods are checked by default.
12
+ # These are customizable with `AllowedMethods` option.
9
13
  #
10
14
  # The `AllowedMethods` option specifies nil-safe methods,
11
15
  # in other words, it is a method that is allowed to skip safe navigation.
@@ -22,6 +26,9 @@ module RuboCop
22
26
  #
23
27
  # @example
24
28
  # # bad
29
+ # CamelCaseConst&.do_something
30
+ #
31
+ # # bad
25
32
  # do_something if attrs&.respond_to?(:[])
26
33
  #
27
34
  # # good
@@ -33,6 +40,9 @@ module RuboCop
33
40
  # end
34
41
  #
35
42
  # # good
43
+ # CamelCaseConst.do_something
44
+ #
45
+ # # good
36
46
  # while node.is_a?(BeginNode)
37
47
  # node = node.parent
38
48
  # end
@@ -57,18 +67,24 @@ module RuboCop
57
67
 
58
68
  NIL_SPECIFIC_METHODS = (nil.methods - Object.new.methods).to_set.freeze
59
69
 
70
+ SNAKE_CASE = /\A[[:digit:][:upper:]_]+\z/.freeze
71
+
60
72
  # @!method respond_to_nil_specific_method?(node)
61
73
  def_node_matcher :respond_to_nil_specific_method?, <<~PATTERN
62
74
  (csend _ :respond_to? (sym %NIL_SPECIFIC_METHODS))
63
75
  PATTERN
64
76
 
77
+ # rubocop:disable Metrics/AbcSize
65
78
  def on_csend(node)
66
- return unless check?(node) && allowed_method?(node.method_name)
67
- return if respond_to_nil_specific_method?(node)
79
+ unless node.receiver.const_type? && !node.receiver.source.match?(SNAKE_CASE)
80
+ return unless check?(node) && allowed_method?(node.method_name)
81
+ return if respond_to_nil_specific_method?(node)
82
+ end
68
83
 
69
84
  range = range_between(node.loc.dot.begin_pos, node.source_range.end_pos)
70
85
  add_offense(range) { |corrector| corrector.replace(node.loc.dot, '.') }
71
86
  end
87
+ # rubocop:enable Metrics/AbcSize
72
88
 
73
89
  private
74
90
 
@@ -12,6 +12,7 @@ module RuboCop
12
12
  # Available are: 'array', 'hash', 'heredoc', and 'method_call'. Each construct
13
13
  # will be counted as one line regardless of its actual size.
14
14
  #
15
+ # NOTE: This cop does not apply for `Struct` definitions.
15
16
  #
16
17
  # NOTE: The `ExcludedMethods` configuration is deprecated and only kept
17
18
  # for backwards compatibility. Please use `AllowedMethods` and `AllowedPatterns`
@@ -40,7 +41,6 @@ module RuboCop
40
41
  # )
41
42
  # end # 6 points
42
43
  #
43
- # NOTE: This cop does not apply for `Struct` definitions.
44
44
  class BlockLength < Base
45
45
  include CodeLength
46
46
  include AllowedMethods
@@ -11,6 +11,8 @@ module RuboCop
11
11
  # Available are: 'array', 'hash', 'heredoc', and 'method_call'. Each construct
12
12
  # will be counted as one line regardless of its actual size.
13
13
  #
14
+ # NOTE: This cop also applies for `Struct` definitions.
15
+ #
14
16
  # @example CountAsOne: ['array', 'heredoc', 'method_call']
15
17
  #
16
18
  # class Foo
@@ -34,8 +36,6 @@ module RuboCop
34
36
  # )
35
37
  # end # 6 points
36
38
  #
37
- #
38
- # NOTE: This cop also applies for `Struct` definitions.
39
39
  class ClassLength < Base
40
40
  include CodeLength
41
41
 
@@ -10,7 +10,7 @@ module RuboCop
10
10
  include Util
11
11
 
12
12
  FOLDABLE_TYPES = %i[array hash heredoc send csend].freeze
13
- CLASSLIKE_TYPES = %i[class module sclass].freeze
13
+ CLASSLIKE_TYPES = %i[class module].freeze
14
14
  private_constant :FOLDABLE_TYPES, :CLASSLIKE_TYPES
15
15
 
16
16
  def initialize(node, processed_source, count_comments: false, foldable_types: [])
@@ -145,7 +145,7 @@ module RuboCop
145
145
 
146
146
  def extract_body(node)
147
147
  case node.type
148
- when :class, :module, :block, :numblock, :def, :defs
148
+ when :class, :module, :sclass, :block, :numblock, :def, :defs
149
149
  node.body
150
150
  when :casgn
151
151
  _scope, _name, value = *node
@@ -8,6 +8,11 @@ module RuboCop
8
8
  # `==`, `equal?`, and `eql?` custom method definitions are allowed by default.
9
9
  # These are customizable with `AllowedMethods` option.
10
10
  #
11
+ # @safety
12
+ # This cop's autocorrection is unsafe because there is no guarantee that
13
+ # the constant `Foo` exists when autocorrecting `var.class.name == 'Foo'` to
14
+ # `var.instance_of?(Foo)`.
15
+ #
11
16
  # @example
12
17
  # # bad
13
18
  # var.class == Date
@@ -4,13 +4,25 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # Enforces the use of a single string formatting utility.
7
- # Valid options include Kernel#format, Kernel#sprintf and String#%.
7
+ # Valid options include `Kernel#format`, `Kernel#sprintf`, and `String#%`.
8
8
  #
9
- # The detection of String#% cannot be implemented in a reliable
9
+ # The detection of `String#%` cannot be implemented in a reliable
10
10
  # manner for all cases, so only two scenarios are considered -
11
11
  # if the first argument is a string literal and if the second
12
12
  # argument is an array literal.
13
13
  #
14
+ # Autocorrection will be applied when using argument is a literal or known built-in conversion
15
+ # methods such as `to_d`, `to_f`, `to_h`, `to_i`, `to_r`, `to_s`, and `to_sym` on variables,
16
+ # provided that their return value is not an array. For example, when using `to_s`,
17
+ # `'%s' % [1, 2, 3].to_s` can be autocorrected without any incompatibility:
18
+ #
19
+ # [source,ruby]
20
+ # ----
21
+ # '%s' % [1, 2, 3] #=> '1'
22
+ # format('%s', [1, 2, 3]) #=> '[1, 2, 3]'
23
+ # '%s' % [1, 2, 3].to_s #=> '[1, 2, 3]'
24
+ # ----
25
+ #
14
26
  # @example EnforcedStyle: format (default)
15
27
  # # bad
16
28
  # puts sprintf('%10s', 'hoge')
@@ -42,6 +54,9 @@ module RuboCop
42
54
  MSG = 'Favor `%<prefer>s` over `%<current>s`.'
43
55
  RESTRICT_ON_SEND = %i[format sprintf %].freeze
44
56
 
57
+ # Known conversion methods whose return value is not an array.
58
+ AUTOCORRECTABLE_METHODS = %i[to_d to_f to_h to_i to_r to_s to_sym].freeze
59
+
45
60
  # @!method formatter(node)
46
61
  def_node_matcher :formatter, <<~PATTERN
47
62
  {
@@ -53,7 +68,7 @@ module RuboCop
53
68
 
54
69
  # @!method variable_argument?(node)
55
70
  def_node_matcher :variable_argument?, <<~PATTERN
56
- (send {str dstr} :% {send_type? lvar_type?})
71
+ (send {str dstr} :% #autocorrectable?)
57
72
  PATTERN
58
73
 
59
74
  def on_send(node)
@@ -70,6 +85,12 @@ module RuboCop
70
85
 
71
86
  private
72
87
 
88
+ def autocorrectable?(node)
89
+ return true if node.lvar_type?
90
+
91
+ node.send_type? && !AUTOCORRECTABLE_METHODS.include?(node.method_name)
92
+ end
93
+
73
94
  def message(detected_style)
74
95
  format(MSG, prefer: method_name(style), current: method_name(detected_style))
75
96
  end
@@ -55,6 +55,25 @@ module RuboCop
55
55
  # foo || raise('exception') if something
56
56
  # ok
57
57
  #
58
+ # # bad
59
+ # define_method(:test) do
60
+ # if something
61
+ # work
62
+ # end
63
+ # end
64
+ #
65
+ # # good
66
+ # define_method(:test) do
67
+ # return unless something
68
+ #
69
+ # work
70
+ # end
71
+ #
72
+ # # also good
73
+ # define_method(:test) do
74
+ # work if something
75
+ # end
76
+ #
58
77
  # @example AllowConsecutiveConditionals: false (default)
59
78
  # # bad
60
79
  # def test
@@ -110,6 +129,13 @@ module RuboCop
110
129
  end
111
130
  alias on_defs on_def
112
131
 
132
+ def on_block(node)
133
+ return unless node.method?(:define_method) || node.method?(:define_singleton_method)
134
+
135
+ on_def(node)
136
+ end
137
+ alias on_numblock on_block
138
+
113
139
  def on_if(node)
114
140
  return if accepted_form?(node)
115
141
 
@@ -136,7 +136,7 @@ module RuboCop
136
136
 
137
137
  private
138
138
 
139
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
139
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
140
140
  def check_branches(node, branches)
141
141
  # return if any branch is empty. An empty branch can be an `if`
142
142
  # without an `else` or a branch that contains only comments.
@@ -149,9 +149,15 @@ module RuboCop
149
149
  branches.any? { |branch| single_child_branch?(branch) }
150
150
 
151
151
  heads = branches.map { |branch| head(branch) }
152
- check_expressions(node, heads, :before_condition) if duplicated_expressions?(node, heads)
152
+
153
+ return unless duplicated_expressions?(node, heads)
154
+
155
+ condition_variable = assignable_condition_value(node)
156
+ return if heads.first.assignment? && condition_variable == heads.first.name.to_s
157
+
158
+ check_expressions(node, heads, :before_condition)
153
159
  end
154
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
160
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
155
161
 
156
162
  def duplicated_expressions?(node, expressions)
157
163
  unique_expressions = expressions.uniq
@@ -164,6 +170,14 @@ module RuboCop
164
170
  node.condition.child_nodes.none? { |n| n.source == lhs.source if n.variable? }
165
171
  end
166
172
 
173
+ def assignable_condition_value(node)
174
+ if node.condition.call_type?
175
+ (receiver = node.condition.receiver) ? receiver.source : node.condition.source
176
+ elsif node.condition.variable?
177
+ node.condition.source
178
+ end
179
+ end
180
+
167
181
  # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
168
182
  def check_expressions(node, expressions, insert_position)
169
183
  return if expressions.any?(&:nil?)
@@ -28,7 +28,7 @@ module RuboCop
28
28
  MSG = 'Avoid multi-line chains of blocks.'
29
29
 
30
30
  def on_block(node)
31
- node.send_node.each_node(:send) do |send_node|
31
+ node.send_node.each_node(:send, :csend) do |send_node|
32
32
  receiver = send_node.receiver
33
33
 
34
34
  next unless (receiver&.block_type? || receiver&.numblock_type?) && receiver&.multiline?
@@ -27,24 +27,16 @@ module RuboCop
27
27
 
28
28
  node.each_descendant(:if).select(&:ternary?).each do |nested_ternary|
29
29
  add_offense(nested_ternary) do |corrector|
30
- if_node = if_node(nested_ternary)
31
- next if part_of_ignored_node?(if_node)
30
+ next if part_of_ignored_node?(node)
32
31
 
33
- autocorrect(corrector, if_node)
34
- ignore_node(if_node)
32
+ autocorrect(corrector, node)
33
+ ignore_node(node)
35
34
  end
36
35
  end
37
36
  end
38
37
 
39
38
  private
40
39
 
41
- def if_node(node)
42
- node = node.parent
43
- return node if node.if_type?
44
-
45
- if_node(node)
46
- end
47
-
48
40
  def autocorrect(corrector, if_node)
49
41
  replace_loc_and_whitespace(corrector, if_node.loc.question, "\n")
50
42
  replace_loc_and_whitespace(corrector, if_node.loc.colon, "\nelse\n")
@@ -114,7 +114,7 @@ module RuboCop
114
114
  if node.parent&.assignment?
115
115
  replace_begin_with_statement(corrector, offense_range, node)
116
116
  else
117
- corrector.remove(offense_range)
117
+ remove_begin(corrector, offense_range, node)
118
118
  end
119
119
 
120
120
  if use_modifier_form_after_multiline_begin_block?(node)
@@ -136,6 +136,14 @@ module RuboCop
136
136
  restore_removed_comments(corrector, offense_range, node, first_child)
137
137
  end
138
138
 
139
+ def remove_begin(corrector, offense_range, node)
140
+ if node.parent.respond_to?(:endless?) && node.parent.endless?
141
+ offense_range = range_with_surrounding_space(offense_range, newlines: true)
142
+ end
143
+
144
+ corrector.remove(offense_range)
145
+ end
146
+
139
147
  # Restore comments that occur between "begin" and "first_child".
140
148
  # These comments will be moved to above the assignment line.
141
149
  def restore_removed_comments(corrector, offense_range, node, first_child)
@@ -13,25 +13,68 @@ module RuboCop
13
13
  # # good
14
14
  # do_something(foo: bar, baz: qux)
15
15
  #
16
+ # # bad
17
+ # do_something(**{foo: bar, baz: qux}.merge(options))
18
+ #
19
+ # # good
20
+ # do_something(foo: bar, baz: qux, **options)
21
+ #
16
22
  class RedundantDoubleSplatHashBraces < Base
17
23
  extend AutoCorrector
18
24
 
19
25
  MSG = 'Remove the redundant double splat and braces, use keyword arguments directly.'
26
+ MERGE_METHODS = %i[merge merge!].freeze
20
27
 
28
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
21
29
  def on_hash(node)
22
30
  return if node.pairs.empty? || node.pairs.any?(&:hash_rocket?)
23
31
  return unless (parent = node.parent)
24
- return unless parent.kwsplat_type?
32
+ return if parent.call_type? && !merge_method?(parent)
33
+ return unless (kwsplat = node.each_ancestor(:kwsplat).first)
34
+ return if allowed_double_splat_receiver?(kwsplat)
25
35
 
26
- add_offense(parent) do |corrector|
27
- corrector.remove(parent.loc.operator)
28
- corrector.remove(opening_brace(node))
29
- corrector.remove(closing_brace(node))
36
+ add_offense(kwsplat) do |corrector|
37
+ autocorrect(corrector, node, kwsplat)
30
38
  end
31
39
  end
40
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
32
41
 
33
42
  private
34
43
 
44
+ def allowed_double_splat_receiver?(kwsplat)
45
+ return false unless kwsplat.children.first.call_type?
46
+
47
+ root_receiver = root_receiver(kwsplat.children.first)
48
+
49
+ !root_receiver&.hash_type?
50
+ end
51
+
52
+ def autocorrect(corrector, node, kwsplat)
53
+ corrector.remove(kwsplat.loc.operator)
54
+ corrector.remove(opening_brace(node))
55
+ corrector.remove(closing_brace(node))
56
+
57
+ merge_methods = select_merge_method_nodes(kwsplat)
58
+ return if merge_methods.empty?
59
+
60
+ autocorrect_merge_methods(corrector, merge_methods, kwsplat)
61
+ end
62
+
63
+ def root_receiver(node)
64
+ receiver = node.receiver
65
+ if receiver&.receiver
66
+ root_receiver(receiver)
67
+ else
68
+ receiver
69
+ end
70
+ end
71
+
72
+ def select_merge_method_nodes(kwsplat)
73
+ extract_send_methods(kwsplat).select do |node|
74
+ merge_method?(node)
75
+ end
76
+ end
77
+
35
78
  def opening_brace(node)
36
79
  node.loc.begin.join(node.children.first.source_range.begin)
37
80
  end
@@ -39,6 +82,44 @@ module RuboCop
39
82
  def closing_brace(node)
40
83
  node.children.last.source_range.end.join(node.loc.end)
41
84
  end
85
+
86
+ def autocorrect_merge_methods(corrector, merge_methods, kwsplat)
87
+ range = range_of_merge_methods(merge_methods)
88
+
89
+ new_kwsplat_arguments = extract_send_methods(kwsplat).map do |descendant|
90
+ convert_to_new_arguments(descendant)
91
+ end
92
+ new_source = new_kwsplat_arguments.compact.reverse.unshift('').join(', ')
93
+
94
+ corrector.replace(range, new_source)
95
+ end
96
+
97
+ def range_of_merge_methods(merge_methods)
98
+ begin_merge_method = merge_methods.last
99
+ end_merge_method = merge_methods.first
100
+
101
+ begin_merge_method.loc.dot.begin.join(end_merge_method.source_range.end)
102
+ end
103
+
104
+ def extract_send_methods(kwsplat)
105
+ kwsplat.each_descendant(:send, :csend)
106
+ end
107
+
108
+ def convert_to_new_arguments(node)
109
+ return unless merge_method?(node)
110
+
111
+ node.arguments.map do |arg|
112
+ if arg.hash_type?
113
+ arg.source
114
+ else
115
+ "**#{arg.source}"
116
+ end
117
+ end
118
+ end
119
+
120
+ def merge_method?(node)
121
+ MERGE_METHODS.include?(node.method_name)
122
+ end
42
123
  end
43
124
  end
44
125
  end
@@ -5,17 +5,21 @@ module RuboCop
5
5
  module Style
6
6
  # Checks for RuntimeError as the argument of raise/fail.
7
7
  #
8
- # It checks for code like this:
9
- #
10
8
  # @example
11
- # # Bad
9
+ # # bad
12
10
  # raise RuntimeError, 'message'
13
- #
14
- # # Bad
15
11
  # raise RuntimeError.new('message')
16
12
  #
17
- # # Good
13
+ # # good
18
14
  # raise 'message'
15
+ #
16
+ # # bad - message is not a string
17
+ # raise RuntimeError, Object.new
18
+ # raise RuntimeError.new(Object.new)
19
+ #
20
+ # # good
21
+ # raise Object.new.to_s
22
+ #
19
23
  class RedundantException < Base
20
24
  extend AutoCorrector
21
25
 
@@ -30,26 +34,42 @@ module RuboCop
30
34
  fix_exploded(node) || fix_compact(node)
31
35
  end
32
36
 
37
+ private
38
+
33
39
  def fix_exploded(node)
34
40
  exploded?(node) do |command, message|
35
41
  add_offense(node, message: MSG_1) do |corrector|
36
- if node.parenthesized?
37
- corrector.replace(node, "#{command}(#{message.source})")
38
- else
39
- corrector.replace(node, "#{command} #{message.source}")
40
- end
42
+ corrector.replace(node, replaced_exploded(node, command, message))
41
43
  end
42
44
  end
43
45
  end
44
46
 
47
+ def replaced_exploded(node, command, message)
48
+ arg = string_message?(message) ? message.source : "#{message.source}.to_s"
49
+ arg = node.parenthesized? ? "(#{arg})" : " #{arg}"
50
+ "#{command}#{arg}"
51
+ end
52
+
53
+ def string_message?(message)
54
+ message.str_type? || message.dstr_type? || message.xstr_type?
55
+ end
56
+
45
57
  def fix_compact(node)
46
58
  compact?(node) do |new_call, message|
47
59
  add_offense(node, message: MSG_2) do |corrector|
48
- corrector.replace(new_call, message.source)
60
+ corrector.replace(new_call, replaced_compact(message))
49
61
  end
50
62
  end
51
63
  end
52
64
 
65
+ def replaced_compact(message)
66
+ if string_message?(message)
67
+ message.source
68
+ else
69
+ "#{message.source}.to_s"
70
+ end
71
+ end
72
+
53
73
  # @!method exploded?(node)
54
74
  def_node_matcher :exploded?, <<~PATTERN
55
75
  (send nil? ${:raise :fail} (const {nil? cbase} :RuntimeError) $_)
@@ -6,6 +6,12 @@ module RuboCop
6
6
  # Identifies usages of `any?`, `empty?` or `none?` predicate methods
7
7
  # chained to `select`/`filter`/`find_all` and change them to use predicate method instead.
8
8
  #
9
+ # @safety
10
+ # This cop's autocorrection is unsafe because `array.select.any?` evaluates all elements
11
+ # through the `select` method, while `array.any?` uses short-circuit evaluation.
12
+ # In other words, `array.select.any?` guarantees the evaluation of every element,
13
+ # but `array.any?` does not necessarily evaluate all of them.
14
+ #
9
15
  # @example
10
16
  # # bad
11
17
  # arr.select { |x| x > 1 }.any?
@@ -28,6 +34,9 @@ module RuboCop
28
34
  # # good
29
35
  # arr.select { |x| x > 1 }.many?
30
36
  #
37
+ # # good
38
+ # arr.select { |x| x > 1 }.present?
39
+ #
31
40
  # @example AllCops:ActiveSupportExtensionsEnabled: true
32
41
  # # bad
33
42
  # arr.select { |x| x > 1 }.many?
@@ -35,12 +44,18 @@ module RuboCop
35
44
  # # good
36
45
  # arr.many? { |x| x > 1 }
37
46
  #
47
+ # # bad
48
+ # arr.select { |x| x > 1 }.present?
49
+ #
50
+ # # good
51
+ # arr.any? { |x| x > 1 }
52
+ #
38
53
  class RedundantFilterChain < Base
39
54
  extend AutoCorrector
40
55
 
41
56
  MSG = 'Use `%<prefer>s` instead of `%<first_method>s.%<second_method>s`.'
42
57
 
43
- RAILS_METHODS = %i[many?].freeze
58
+ RAILS_METHODS = %i[many? present?].freeze
44
59
  RESTRICT_ON_SEND = (%i[any? empty? none? one?] + RAILS_METHODS).freeze
45
60
 
46
61
  # @!method select_predicate?(node)
@@ -58,7 +73,8 @@ module RuboCop
58
73
  empty?: :none?,
59
74
  none?: :none?,
60
75
  one?: :one?,
61
- many?: :many?
76
+ many?: :many?,
77
+ present?: :any?
62
78
  }.freeze
63
79
  private_constant :REPLACEMENT_METHODS
64
80
 
@@ -126,16 +126,32 @@ module RuboCop
126
126
 
127
127
  def check(begin_node)
128
128
  node = begin_node.children.first
129
- return offense(begin_node, 'a keyword') if keyword_with_redundant_parentheses?(node)
130
- return offense(begin_node, 'a literal') if disallowed_literal?(begin_node, node)
131
- return offense(begin_node, 'a variable') if node.variable?
132
- return offense(begin_node, 'a constant') if node.const_type?
133
129
 
134
- return offense(begin_node, 'an interpolated expression') if interpolation?(begin_node)
130
+ if (message = find_offense_message(begin_node, node))
131
+ return offense(begin_node, message)
132
+ end
135
133
 
136
134
  check_send(begin_node, node) if node.call_type?
137
135
  end
138
136
 
137
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
138
+ def find_offense_message(begin_node, node)
139
+ return 'a keyword' if keyword_with_redundant_parentheses?(node)
140
+ return 'a literal' if disallowed_literal?(begin_node, node)
141
+ return 'a variable' if node.variable?
142
+ return 'a constant' if node.const_type?
143
+ return 'an interpolated expression' if interpolation?(begin_node)
144
+
145
+ return if begin_node.chained? || !begin_node.parent.nil?
146
+
147
+ if node.and_type? || node.or_type?
148
+ 'a logical expression'
149
+ elsif node.respond_to?(:comparison_method?) && node.comparison_method?
150
+ 'a comparison expression'
151
+ end
152
+ end
153
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
154
+
139
155
  # @!method interpolation?(node)
140
156
  def_node_matcher :interpolation?, '[^begin ^^dstr]'
141
157
 
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for single-line `do`...`end` block.
7
+ #
8
+ # In practice a single line `do`...`end` is autocorrected when `EnforcedStyle: semantic`
9
+ # in `Style/BlockDelimiters`. The autocorrection maintains the `do` ... `end` syntax to
10
+ # preserve semantics and does not change it to `{`...`}` block.
11
+ #
12
+ # @example
13
+ #
14
+ # # bad
15
+ # foo do |arg| bar(arg) end
16
+ #
17
+ # # good
18
+ # foo do |arg|
19
+ # bar(arg)
20
+ # end
21
+ #
22
+ # # bad
23
+ # ->(arg) do bar(arg) end
24
+ #
25
+ # # good
26
+ # ->(arg) { bar(arg) }
27
+ #
28
+ class SingleLineDoEndBlock < Base
29
+ extend AutoCorrector
30
+
31
+ MSG = 'Prefer multiline `do`...`end` block.'
32
+
33
+ def on_block(node)
34
+ return if !node.single_line? || node.braces?
35
+
36
+ add_offense(node) do |corrector|
37
+ corrector.insert_after(do_line(node), "\n")
38
+
39
+ node_body = node.body
40
+
41
+ if node_body.respond_to?(:heredoc?) && node_body.heredoc?
42
+ corrector.remove(node.loc.end)
43
+ corrector.insert_after(node_body.loc.heredoc_end, "\nend")
44
+ else
45
+ corrector.insert_after(node_body, "\n")
46
+ end
47
+ end
48
+ end
49
+ alias on_numblock on_block
50
+
51
+ private
52
+
53
+ def do_line(node)
54
+ if node.numblock_type? || node.arguments.children.empty? || node.send_node.lambda_literal?
55
+ node.loc.begin
56
+ else
57
+ node.arguments
58
+ end
59
+ end
60
+
61
+ def x(corrector, node); end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'fileutils'
3
4
  require 'pathname'
4
5
  require_relative '../cache_config'
5
6
  require_relative '../config_finder'
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '1.56.4'
6
+ STRING = '1.57.1'
7
7
 
8
8
  MSG = '%<version>s (using Parser %<parser_version>s, ' \
9
9
  'rubocop-ast %<rubocop_ast_version>s, ' \
data/lib/rubocop.rb CHANGED
@@ -580,6 +580,7 @@ require_relative 'rubocop/cop/style/redundant_regexp_constructor'
580
580
  require_relative 'rubocop/cop/style/redundant_self_assignment'
581
581
  require_relative 'rubocop/cop/style/redundant_self_assignment_branch'
582
582
  require_relative 'rubocop/cop/style/require_order'
583
+ require_relative 'rubocop/cop/style/single_line_do_end_block'
583
584
  require_relative 'rubocop/cop/style/sole_nested_conditional'
584
585
  require_relative 'rubocop/cop/style/static_class'
585
586
  require_relative 'rubocop/cop/style/map_compact_with_conditional_block'
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.56.4
4
+ version: 1.57.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
8
8
  - Jonas Arvidsson
9
9
  - Yuji Nakayama
10
- autorequire:
10
+ autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2023-09-28 00:00:00.000000000 Z
13
+ date: 2023-10-13 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: base64
@@ -74,14 +74,14 @@ dependencies:
74
74
  requirements:
75
75
  - - ">="
76
76
  - !ruby/object:Gem::Version
77
- version: 3.2.2.3
77
+ version: 3.2.2.4
78
78
  type: :runtime
79
79
  prerelease: false
80
80
  version_requirements: !ruby/object:Gem::Requirement
81
81
  requirements:
82
82
  - - ">="
83
83
  - !ruby/object:Gem::Version
84
- version: 3.2.2.3
84
+ version: 3.2.2.4
85
85
  - !ruby/object:Gem::Dependency
86
86
  name: rainbow
87
87
  requirement: !ruby/object:Gem::Requirement
@@ -895,6 +895,7 @@ files:
895
895
  - lib/rubocop/cop/style/signal_exception.rb
896
896
  - lib/rubocop/cop/style/single_argument_dig.rb
897
897
  - lib/rubocop/cop/style/single_line_block_params.rb
898
+ - lib/rubocop/cop/style/single_line_do_end_block.rb
898
899
  - lib/rubocop/cop/style/single_line_methods.rb
899
900
  - lib/rubocop/cop/style/slicing_with_range.rb
900
901
  - lib/rubocop/cop/style/sole_nested_conditional.rb
@@ -1038,10 +1039,10 @@ metadata:
1038
1039
  homepage_uri: https://rubocop.org/
1039
1040
  changelog_uri: https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md
1040
1041
  source_code_uri: https://github.com/rubocop/rubocop/
1041
- documentation_uri: https://docs.rubocop.org/rubocop/1.56/
1042
+ documentation_uri: https://docs.rubocop.org/rubocop/1.57/
1042
1043
  bug_tracker_uri: https://github.com/rubocop/rubocop/issues
1043
1044
  rubygems_mfa_required: 'true'
1044
- post_install_message:
1045
+ post_install_message:
1045
1046
  rdoc_options: []
1046
1047
  require_paths:
1047
1048
  - lib
@@ -1056,8 +1057,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
1056
1057
  - !ruby/object:Gem::Version
1057
1058
  version: '0'
1058
1059
  requirements: []
1059
- rubygems_version: 3.4.6
1060
- signing_key:
1060
+ rubygems_version: 3.3.7
1061
+ signing_key:
1061
1062
  specification_version: 4
1062
1063
  summary: Automatic Ruby code style checking tool.
1063
1064
  test_files: []