rubocop 1.25.1 → 1.26.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +14 -4
  4. data/lib/rubocop/cli.rb +1 -1
  5. data/lib/rubocop/cop/badge.rb +7 -1
  6. data/lib/rubocop/cop/gemspec/require_mfa.rb +4 -3
  7. data/lib/rubocop/cop/generator.rb +2 -7
  8. data/lib/rubocop/cop/internal_affairs/redundant_context_config_parameter.rb +46 -0
  9. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  10. data/lib/rubocop/cop/layout/case_indentation.rb +1 -1
  11. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +7 -8
  12. data/lib/rubocop/cop/lint/inherit_exception.rb +19 -28
  13. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +3 -2
  14. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +5 -0
  15. data/lib/rubocop/cop/lint/symbol_conversion.rb +3 -2
  16. data/lib/rubocop/cop/lint/useless_times.rb +13 -9
  17. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +1 -1
  18. data/lib/rubocop/cop/mixin/line_length_help.rb +17 -6
  19. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +3 -1
  20. data/lib/rubocop/cop/security/yaml_load.rb +9 -3
  21. data/lib/rubocop/cop/style/def_with_parentheses.rb +16 -11
  22. data/lib/rubocop/cop/style/for.rb +4 -0
  23. data/lib/rubocop/cop/style/lambda_call.rb +12 -20
  24. data/lib/rubocop/cop/style/nested_file_dirname.rb +66 -0
  25. data/lib/rubocop/cop/style/optional_boolean_parameter.rb +3 -2
  26. data/lib/rubocop/cop/style/select_by_regexp.rb +6 -1
  27. data/lib/rubocop/cop/style/sole_nested_conditional.rb +50 -12
  28. data/lib/rubocop/cop/style/string_concatenation.rb +7 -1
  29. data/lib/rubocop/cop/style/trailing_comma_in_array_literal.rb +1 -1
  30. data/lib/rubocop/cop/style/trailing_comma_in_hash_literal.rb +1 -1
  31. data/lib/rubocop/cop/style/unless_else.rb +4 -0
  32. data/lib/rubocop/cop/variable_force.rb +1 -5
  33. data/lib/rubocop/cops_documentation_generator.rb +2 -2
  34. data/lib/rubocop/formatter/disabled_config_formatter.rb +2 -2
  35. data/lib/rubocop/options.rb +8 -2
  36. data/lib/rubocop/rspec/shared_contexts.rb +4 -0
  37. data/lib/rubocop/runner.rb +1 -1
  38. data/lib/rubocop/target_ruby.rb +1 -1
  39. data/lib/rubocop/version.rb +1 -1
  40. data/lib/rubocop.rb +1 -0
  41. metadata +7 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 54b88752efa8bb84dc034391460b9071f31179dcb279550a209d31829e7b8c93
4
- data.tar.gz: 31115cff5fdbf562dee62cf7e07033205d150b61ac5fd11476708f21e67000cf
3
+ metadata.gz: 10e6b8905d81b2204fd3d0bf60d3278cd902d0cefc2ed387bf2c317062b46034
4
+ data.tar.gz: f27011464d465728f1fa470c85c4779c2b148b3cdc872247c5a25d421f269076
5
5
  SHA512:
6
- metadata.gz: acdaf069659a775853b20e05734e85cd65c0c8734b09e568d4360dc9a7440a49ec8e47928b0da78866e1326a588638d18f2564e6e94023bb17b2f39493555027
7
- data.tar.gz: 2765d9c9c10966c485be6cd70190b69a25aa99800c8d483bd67c1fdfe3dc5d13bb97cb1ca7f9f663776869acda5a426bc797f3b40adda17e0241ff8b62a374f1
6
+ metadata.gz: 215ee2bb6f0699fdedcd82c7795c40e2350c33fdfbd48117dbb2fdcaa957bcbff9a65ad9c95b9666d4b9c70d6a2e2d8b6ec6904c3c3f01cbbfa8fee2319c9518
7
+ data.tar.gz: 0b33b7c3aecf2b7482e19cdbf1a94a56f21d3cb14035c68c62ce0572385edb34a59faa1ea5fb21d9504b04e18f797b0419c6ade76ff70604311b9f899e28b39d
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.25', require: false
56
+ gem 'rubocop', '~> 1.26', 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
@@ -262,7 +262,7 @@ Gemspec/OrderedDependencies:
262
262
  - '**/*.gemspec'
263
263
 
264
264
  Gemspec/RequireMFA:
265
- Description: 'Checks that the gemspec has metadata to require MFA from RubyGems.'
265
+ Description: 'Checks that the gemspec has metadata to require Multi-Factor Authentication from RubyGems.'
266
266
  Enabled: pending
267
267
  VersionAdded: '1.23'
268
268
  Reference:
@@ -1816,12 +1816,14 @@ Lint/IneffectiveAccessModifier:
1816
1816
  Lint/InheritException:
1817
1817
  Description: 'Avoid inheriting from the `Exception` class.'
1818
1818
  Enabled: true
1819
+ SafeAutoCorrect: false
1819
1820
  VersionAdded: '0.41'
1821
+ VersionChanged: '1.26'
1820
1822
  # The default base class in favour of `Exception`.
1821
- EnforcedStyle: runtime_error
1823
+ EnforcedStyle: standard_error
1822
1824
  SupportedStyles:
1823
- - runtime_error
1824
1825
  - standard_error
1826
+ - runtime_error
1825
1827
 
1826
1828
  Lint/InterpolationCheck:
1827
1829
  Description: 'Raise warning for interpolation in single q strs.'
@@ -2011,6 +2013,8 @@ Lint/RedundantDirGlobSort:
2011
2013
  Description: 'Checks for redundant `sort` method to `Dir.glob` and `Dir[]`.'
2012
2014
  Enabled: pending
2013
2015
  VersionAdded: '1.8'
2016
+ VersionChanged: '1.26'
2017
+ SafeAutoCorrect: false
2014
2018
 
2015
2019
  Lint/RedundantRequireStatement:
2016
2020
  Description: 'Checks for unnecessary `require` statement.'
@@ -3542,8 +3546,9 @@ Style/For:
3542
3546
  Description: 'Checks use of for or each in multiline loops.'
3543
3547
  StyleGuide: '#no-for-loops'
3544
3548
  Enabled: true
3549
+ SafeAutoCorrect: false
3545
3550
  VersionAdded: '0.13'
3546
- VersionChanged: '0.59'
3551
+ VersionChanged: '1.26'
3547
3552
  EnforcedStyle: each
3548
3553
  SupportedStyles:
3549
3554
  - each
@@ -4122,6 +4127,11 @@ Style/NegatedWhile:
4122
4127
  Enabled: true
4123
4128
  VersionAdded: '0.20'
4124
4129
 
4130
+ Style/NestedFileDirname:
4131
+ Description: 'Checks for nested `File.dirname`.'
4132
+ Enabled: pending
4133
+ VersionAdded: '1.26'
4134
+
4125
4135
  Style/NestedModifier:
4126
4136
  Description: 'Avoid using nested modifiers.'
4127
4137
  StyleGuide: '#no-nested-modifiers'
data/lib/rubocop/cli.rb CHANGED
@@ -14,7 +14,7 @@ module RuboCop
14
14
  ignore_disable_comments lint only only_guide_cops require safe
15
15
  ].freeze
16
16
 
17
- class Finished < RuntimeError; end
17
+ class Finished < StandardError; end
18
18
 
19
19
  attr_reader :options, :config_store
20
20
 
@@ -19,7 +19,13 @@ module RuboCop
19
19
  end
20
20
 
21
21
  def self.parse(identifier)
22
- new(identifier.split('/'))
22
+ new(identifier.split('/').map { |i| camel_case(i) })
23
+ end
24
+
25
+ def self.camel_case(name_part)
26
+ return 'RSpec' if name_part == 'rspec'
27
+
28
+ name_part.gsub(/^\w|_\w/) { |match| match[-1, 1].upcase }
23
29
  end
24
30
 
25
31
  def initialize(class_name_parts)
@@ -5,9 +5,10 @@ module RuboCop
5
5
  module Gemspec
6
6
  # Requires a gemspec to have `rubygems_mfa_required` metadata set.
7
7
  #
8
- # This setting tells RubyGems that MFA is required for accounts to
9
- # be able perform privileged operations, such as (see
10
- # RubyGems' documentation for the full list of privileged operations):
8
+ # This setting tells RubyGems that MFA (Multi-Factor Authentication) is
9
+ # required for accounts to be able perform privileged operations, such as
10
+ # (see RubyGems' documentation for the full list of privileged
11
+ # operations):
11
12
  #
12
13
  # * `gem push`
13
14
  # * `gem yank`
@@ -8,13 +8,7 @@ module RuboCop
8
8
  # and spec file when given a valid qualified cop name.
9
9
  # @api private
10
10
  class Generator
11
- # NOTE: RDoc 5.1.0 or lower has the following issue.
12
- # https://github.com/rubocop/rubocop/issues/7043
13
- #
14
- # The following `String#gsub` can be replaced with
15
- # squiggly heredoc when RuboCop supports Ruby 2.5 or higher
16
- # (RDoc 6.0 or higher).
17
- SOURCE_TEMPLATE = <<-RUBY.gsub(/^ {8}/, '')
11
+ SOURCE_TEMPLATE = <<~RUBY
18
12
  # frozen_string_literal: true
19
13
 
20
14
  module RuboCop
@@ -68,6 +62,7 @@ module RuboCop
68
62
  # For example
69
63
  MSG = 'Use `#good_method` instead of `#bad_method`.'
70
64
 
65
+ # @!method bad_method?(node)
71
66
  def_node_matcher :bad_method?, <<~PATTERN
72
67
  (send nil? :bad_method ...)
73
68
  PATTERN
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module InternalAffairs
6
+ # Checks for redundant `:config` parameter in the `context` arguments.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # context 'foo', :config do
12
+ # end
13
+ #
14
+ # # good
15
+ # context 'foo' do
16
+ # end
17
+ #
18
+ class RedundantContextConfigParameter < Base
19
+ include RangeHelp
20
+ extend AutoCorrector
21
+
22
+ MSG = 'Remove the redundant `:config` parameter.'
23
+ RESTRICT_ON_SEND = %i[context].freeze
24
+
25
+ def on_send(node)
26
+ arguments = node.arguments
27
+ config_node = arguments.detect { |argument| argument.source == ':config' }
28
+ return unless config_node
29
+
30
+ add_offense(config_node) do |corrector|
31
+ dup_arguments = arguments.dup
32
+ dup_arguments.delete(config_node)
33
+
34
+ corrector.replace(offense_range(arguments), dup_arguments.map(&:source).join(', '))
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def offense_range(arguments)
41
+ range_between(arguments.first.source_range.begin_pos, arguments.last.source_range.end_pos)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -9,6 +9,7 @@ require_relative 'internal_affairs/node_destructuring'
9
9
  require_relative 'internal_affairs/node_matcher_directive'
10
10
  require_relative 'internal_affairs/node_type_predicate'
11
11
  require_relative 'internal_affairs/offense_location_keyword'
12
+ require_relative 'internal_affairs/redundant_context_config_parameter'
12
13
  require_relative 'internal_affairs/redundant_described_class_as_subject'
13
14
  require_relative 'internal_affairs/redundant_let_rubocop_config_new'
14
15
  require_relative 'internal_affairs/redundant_location_argument'
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Layout
6
- # This cop checks how the `when` and `in`s of a `case` expression
6
+ # This cop checks how the `when` and ``in``s of a `case` expression
7
7
  # are indented in relation to its `case` or `end` keyword.
8
8
  #
9
9
  # It will register a separate offense for each misaligned `when` and `in`.
@@ -3,27 +3,26 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Layout
6
- # This cop checks the indentation of the right hand side operand in
7
- # binary operations that span more than one line.
6
+ # This cop checks the indentation of the right hand side operand in binary operations that
7
+ # span more than one line.
8
8
  #
9
- # The `aligned` style checks that operators are aligned if they are part
10
- # of an `if` or `while` condition, a `return` statement, etc. In other
11
- # contexts, the second operand should be indented regardless of enforced
12
- # style.
9
+ # The `aligned` style checks that operators are aligned if they are part of an `if` or `while`
10
+ # condition, an explicit `return` statement, etc. In other contexts, the second operand should
11
+ # be indented regardless of enforced style.
13
12
  #
14
13
  # @example EnforcedStyle: aligned (default)
15
14
  # # bad
16
15
  # if a +
17
16
  # b
18
17
  # something &&
19
- # something_else
18
+ # something_else
20
19
  # end
21
20
  #
22
21
  # # good
23
22
  # if a +
24
23
  # b
25
24
  # something &&
26
- # something_else
25
+ # something_else
27
26
  # end
28
27
  #
29
28
  # @example EnforcedStyle: indented
@@ -3,12 +3,16 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # This cop looks for error classes inheriting from `Exception`
7
- # and its standard library subclasses, excluding subclasses of
8
- # `StandardError`. It is configurable to suggest using either
9
- # `RuntimeError` (default) or `StandardError` instead.
6
+ # This cop looks for error classes inheriting from `Exception`.
7
+ # It is configurable to suggest using either `StandardError` (default) or
8
+ # `RuntimeError` instead.
10
9
  #
11
- # @example EnforcedStyle: runtime_error (default)
10
+ # @safety
11
+ # This cop's autocorrection is unsafe because `rescue` that omit
12
+ # exception class handle `StandardError` and its subclasses,
13
+ # but not `Exception` and its subclasses.
14
+ #
15
+ # @example EnforcedStyle: standard_error (default)
12
16
  # # bad
13
17
  #
14
18
  # class C < Exception; end
@@ -17,11 +21,11 @@ module RuboCop
17
21
  #
18
22
  # # good
19
23
  #
20
- # class C < RuntimeError; end
24
+ # class C < StandardError; end
21
25
  #
22
- # C = Class.new(RuntimeError)
26
+ # C = Class.new(StandardError)
23
27
  #
24
- # @example EnforcedStyle: standard_error
28
+ # @example EnforcedStyle: runtime_error
25
29
  # # bad
26
30
  #
27
31
  # class C < Exception; end
@@ -30,31 +34,18 @@ module RuboCop
30
34
  #
31
35
  # # good
32
36
  #
33
- # class C < StandardError; end
37
+ # class C < RuntimeError; end
34
38
  #
35
- # C = Class.new(StandardError)
39
+ # C = Class.new(RuntimeError)
36
40
  class InheritException < Base
37
41
  include ConfigurableEnforcedStyle
38
42
  extend AutoCorrector
39
43
 
40
- MSG = 'Inherit from `%<prefer>s` instead of `%<current>s`.'
44
+ MSG = 'Inherit from `%<prefer>s` instead of `Exception`.'
41
45
  PREFERRED_BASE_CLASS = {
42
46
  runtime_error: 'RuntimeError',
43
47
  standard_error: 'StandardError'
44
48
  }.freeze
45
- ILLEGAL_CLASSES = %w[
46
- Exception
47
- SystemStackError
48
- NoMemoryError
49
- SecurityError
50
- NotImplementedError
51
- LoadError
52
- SyntaxError
53
- ScriptError
54
- Interrupt
55
- SignalException
56
- SystemExit
57
- ].freeze
58
49
 
59
50
  RESTRICT_ON_SEND = %i[new].freeze
60
51
 
@@ -66,7 +57,7 @@ module RuboCop
66
57
  PATTERN
67
58
 
68
59
  def on_class(node)
69
- return unless node.parent_class && illegal_class_name?(node.parent_class)
60
+ return unless node.parent_class && exception_class?(node.parent_class)
70
61
 
71
62
  message = message(node.parent_class)
72
63
 
@@ -77,7 +68,7 @@ module RuboCop
77
68
 
78
69
  def on_send(node)
79
70
  constant = class_new_call?(node)
80
- return unless constant && illegal_class_name?(constant)
71
+ return unless constant && exception_class?(constant)
81
72
 
82
73
  message = message(constant)
83
74
 
@@ -92,8 +83,8 @@ module RuboCop
92
83
  format(MSG, prefer: preferred_base_class, current: node.const_name)
93
84
  end
94
85
 
95
- def illegal_class_name?(class_node)
96
- ILLEGAL_CLASSES.include?(class_node.const_name)
86
+ def exception_class?(class_node)
87
+ class_node.const_name == 'Exception'
97
88
  end
98
89
 
99
90
  def preferred_base_class
@@ -19,7 +19,7 @@ module RuboCop
19
19
  include RangeHelp
20
20
  extend AutoCorrector
21
21
 
22
- MSG = '`(...)` interpreted as grouped expression.'
22
+ MSG = '`%<argument>s` interpreted as grouped expression.'
23
23
 
24
24
  def on_send(node)
25
25
  return if valid_context?(node)
@@ -28,8 +28,9 @@ module RuboCop
28
28
  return unless space_length.positive?
29
29
 
30
30
  range = space_range(node.first_argument.source_range, space_length)
31
+ message = format(MSG, argument: node.first_argument.source)
31
32
 
32
- add_offense(range) { |corrector| corrector.remove(range) }
33
+ add_offense(range, message: message) { |corrector| corrector.remove(range) }
33
34
  end
34
35
  alias on_csend on_send
35
36
 
@@ -6,6 +6,11 @@ module RuboCop
6
6
  # Sort globbed results by default in Ruby 3.0.
7
7
  # This cop checks for redundant `sort` method to `Dir.glob` and `Dir[]`.
8
8
  #
9
+ # @safety
10
+ # This cop is unsafe, in case of having a file and a directory with
11
+ # identical names, since directory will be loaded before the file, which
12
+ # will break `exe/files.rb` that rely on `exe.rb` file.
13
+ #
9
14
  # @example
10
15
  #
11
16
  # # bad
@@ -147,13 +147,14 @@ module RuboCop
147
147
  # will be ignored.
148
148
  return unless node.value.to_s.match?(/\A[a-z0-9_]/i)
149
149
 
150
- correction = node.value.inspect.delete_prefix(':')
150
+ correction = node.value.inspect
151
+ correction = correction.delete_prefix(':') if node.parent.colon?
151
152
  return if properly_quoted?(node.source, correction)
152
153
 
153
154
  register_offense(
154
155
  node,
155
156
  correction: correction,
156
- message: format(MSG, correction: "#{correction}:")
157
+ message: format(MSG, correction: node.parent.colon? ? "#{correction}:" : correction)
157
158
  )
158
159
  end
159
160
 
@@ -51,20 +51,24 @@ module RuboCop
51
51
  node = node.block_node if node.block_literal?
52
52
 
53
53
  add_offense(node, message: format(MSG, count: count)) do |corrector|
54
- next unless own_line?(node)
55
-
56
- if never_process?(count, node)
57
- remove_node(corrector, node)
58
- elsif !proc_name.empty?
59
- autocorrect_block_pass(corrector, node, proc_name)
60
- else
61
- autocorrect_block(corrector, node)
62
- end
54
+ next if !own_line?(node) || node.parent&.send_type?
55
+
56
+ autocorrect(corrector, count, node, proc_name)
63
57
  end
64
58
  end
65
59
 
66
60
  private
67
61
 
62
+ def autocorrect(corrector, count, node, proc_name)
63
+ if never_process?(count, node)
64
+ remove_node(corrector, node)
65
+ elsif !proc_name.empty?
66
+ autocorrect_block_pass(corrector, node, proc_name)
67
+ else
68
+ autocorrect_block(corrector, node)
69
+ end
70
+ end
71
+
68
72
  def never_process?(count, node)
69
73
  count < 1 || (node.block_type? && node.body.nil?)
70
74
  end
@@ -41,7 +41,7 @@ module RuboCop
41
41
  end
42
42
 
43
43
  def require_hash_value?(hash_key_source, node)
44
- return true if require_hash_value_for_around_hash_literal?(node)
44
+ return true if !node.key.sym_type? || require_hash_value_for_around_hash_literal?(node)
45
45
 
46
46
  hash_value = node.value
47
47
  return true unless hash_value.send_type? || hash_value.lvar_type?
@@ -39,12 +39,7 @@ module RuboCop
39
39
  pos + indentation_difference(line)
40
40
  end
41
41
 
42
- # Extend the end position until the start of the next word, if any.
43
- # This allows for URIs that are wrapped in quotes or parens to be handled properly
44
- # while not allowing additional words to be added after the URL.
45
- if (match = line[end_position..line_length(line)]&.match(/^\S+(?=\s|$)/))
46
- end_position += match.offset(0).last
47
- end
42
+ end_position = extend_uri_end_position(line, end_position)
48
43
 
49
44
  return nil if begin_position < max_line_length && end_position < max_line_length
50
45
 
@@ -65,6 +60,22 @@ module RuboCop
65
60
  (line.index(/[^\t]/) || 0) * (tab_indentation_width - 1)
66
61
  end
67
62
 
63
+ def extend_uri_end_position(line, end_position)
64
+ # Extend the end position YARD comments with linked URLs of the form {<uri> <title>}
65
+ if line&.match(/{(\s|\S)*}$/)
66
+ match = line[end_position..line_length(line)]&.match(/(\s|\S)*}/)
67
+ end_position += match.offset(0).last
68
+ end
69
+
70
+ # Extend the end position until the start of the next word, if any.
71
+ # This allows for URIs that are wrapped in quotes or parens to be handled properly
72
+ # while not allowing additional words to be added after the URL.
73
+ if (match = line[end_position..line_length(line)]&.match(/^\S+(?=\s|$)/))
74
+ end_position += match.offset(0).last
75
+ end
76
+ end_position
77
+ end
78
+
68
79
  def tab_indentation_width
69
80
  config.for_cop('Layout/IndentationStyle')['IndentationWidth'] ||
70
81
  config.for_cop('Layout/IndentationWidth')['Width']
@@ -29,7 +29,9 @@ module RuboCop
29
29
  # b c { block }. <-- b is indented relative to a
30
30
  # d <-- d is indented relative to a
31
31
  def left_hand_side(lhs)
32
- lhs = lhs.parent while lhs.parent&.send_type? && lhs.parent.loc.dot
32
+ while lhs.parent&.send_type? && lhs.parent.loc.dot && !lhs.parent.assignment_method?
33
+ lhs = lhs.parent
34
+ end
33
35
  lhs
34
36
  end
35
37
 
@@ -7,17 +7,21 @@ module RuboCop
7
7
  # potential security issues leading to remote code execution when
8
8
  # loading from an untrusted source.
9
9
  #
10
+ # NOTE: Ruby 3.1+ (Psych 4) uses `Psych.load` as `Psych.safe_load` by default.
11
+ #
10
12
  # @safety
11
13
  # The behaviour of the code might change depending on what was
12
14
  # in the YAML payload, since `YAML.safe_load` is more restrictive.
13
15
  #
14
16
  # @example
15
17
  # # bad
16
- # YAML.load("--- foo")
18
+ # YAML.load("--- !ruby/object:Foo {}") # Psych 3 is unsafe by default
17
19
  #
18
20
  # # good
19
- # YAML.safe_load("--- foo")
20
- # YAML.dump("foo")
21
+ # YAML.safe_load("--- !ruby/object:Foo {}", [Foo]) # Ruby 2.5 (Psych 3)
22
+ # YAML.safe_load("--- !ruby/object:Foo {}", permitted_classes: [Foo]) # Ruby 3.0- (Psych 3)
23
+ # YAML.load("--- !ruby/object:Foo {}", permitted_classes: [Foo]) # Ruby 3.1+ (Psych 4)
24
+ # YAML.dump(foo)
21
25
  #
22
26
  class YAMLLoad < Base
23
27
  extend AutoCorrector
@@ -31,6 +35,8 @@ module RuboCop
31
35
  PATTERN
32
36
 
33
37
  def on_send(node)
38
+ return if target_ruby_version >= 3.1
39
+
34
40
  yaml_load(node) do
35
41
  add_offense(node.loc.selector) do |corrector|
36
42
  corrector.replace(node.loc.selector, 'safe_load')
@@ -11,27 +11,33 @@ module RuboCop
11
11
  #
12
12
  # # bad
13
13
  # def foo()
14
- # # does a thing
14
+ # do_something
15
15
  # end
16
16
  #
17
17
  # # good
18
18
  # def foo
19
- # # does a thing
19
+ # do_something
20
20
  # end
21
21
  #
22
- # # also good
23
- # def foo() does_a_thing end
22
+ # # bad
23
+ # def foo() = do_something
24
+ #
25
+ # # good
26
+ # def foo = do_something
27
+ #
28
+ # # good (without parentheses it's a syntax error)
29
+ # def foo() do_something end
24
30
  #
25
31
  # @example
26
32
  #
27
33
  # # bad
28
34
  # def Baz.foo()
29
- # # does a thing
35
+ # do_something
30
36
  # end
31
37
  #
32
38
  # # good
33
39
  # def Baz.foo
34
- # # does a thing
40
+ # do_something
35
41
  # end
36
42
  class DefWithParentheses < Base
37
43
  extend AutoCorrector
@@ -39,12 +45,11 @@ module RuboCop
39
45
  MSG = "Omit the parentheses in defs when the method doesn't accept any arguments."
40
46
 
41
47
  def on_def(node)
42
- return if node.single_line?
43
- return unless !node.arguments? && (node_arguments_loc_begin = node.arguments.loc.begin)
48
+ return if node.single_line? && !node.endless?
49
+ return unless !node.arguments? && (node_arguments = node.arguments.source_range)
44
50
 
45
- add_offense(node_arguments_loc_begin) do |corrector|
46
- corrector.remove(node_arguments_loc_begin)
47
- corrector.remove(node.arguments.loc.end)
51
+ add_offense(node_arguments) do |corrector|
52
+ corrector.remove(node_arguments)
48
53
  end
49
54
  end
50
55
  alias on_defs on_def
@@ -38,6 +38,10 @@ module RuboCop
38
38
  # end
39
39
  # end
40
40
  #
41
+ # @safety
42
+ # This cop's autocorrection is unsafe because the scope of
43
+ # variables is different between `each` and `for`.
44
+ #
41
45
  class For < Base
42
46
  include ConfigurableEnforcedStyle
43
47
  include RangeHelp
@@ -22,45 +22,37 @@ module RuboCop
22
22
  include ConfigurableEnforcedStyle
23
23
  extend AutoCorrector
24
24
 
25
+ MSG = 'Prefer the use of `%<prefer>s` over `%<current>s`.'
25
26
  RESTRICT_ON_SEND = %i[call].freeze
26
27
 
27
28
  def on_send(node)
28
29
  return unless node.receiver
29
30
 
30
31
  if offense?(node)
31
- add_offense(node) do |corrector|
32
+ prefer = prefer(node)
33
+ current = node.source
34
+
35
+ add_offense(node, message: format(MSG, prefer: prefer, current: current)) do |corrector|
32
36
  opposite_style_detected
33
- autocorrect(corrector, node)
37
+ corrector.replace(node, prefer)
34
38
  end
35
39
  else
36
40
  correct_style_detected
37
41
  end
38
42
  end
39
43
 
40
- def autocorrect(corrector, node)
41
- if explicit_style?
42
- receiver = node.receiver.source
43
- replacement = node.source.sub("#{receiver}.", "#{receiver}.call")
44
-
45
- corrector.replace(node, replacement)
46
- else
47
- add_parentheses(node, corrector) unless node.parenthesized?
48
- corrector.remove(node.loc.selector)
49
- end
50
- end
51
-
52
44
  private
53
45
 
54
46
  def offense?(node)
55
47
  (explicit_style? && node.implicit_call?) || (implicit_style? && !node.implicit_call?)
56
48
  end
57
49
 
58
- def message(_node)
59
- if explicit_style?
60
- 'Prefer the use of `lambda.call(...)` over `lambda.(...)`.'
61
- else
62
- 'Prefer the use of `lambda.(...)` over `lambda.call(...)`.'
63
- end
50
+ def prefer(node)
51
+ receiver = node.receiver.source
52
+ arguments = node.arguments.map(&:source).join(', ')
53
+ method = explicit_style? ? "call(#{arguments})" : "(#{arguments})"
54
+
55
+ "#{receiver}.#{method}"
64
56
  end
65
57
 
66
58
  def implicit_style?
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop checks for nested `File.dirname`.
7
+ # It replaces nested `File.dirname` with the level argument introduced in Ruby 3.1.
8
+ #
9
+ # @example
10
+ #
11
+ # # bad
12
+ # File.dirname(File.dirname(path))
13
+ #
14
+ # # good
15
+ # File.dirname(path, 2)
16
+ #
17
+ class NestedFileDirname < Base
18
+ include RangeHelp
19
+ extend AutoCorrector
20
+ extend TargetRubyVersion
21
+
22
+ MSG = 'Use `dirname(%<path>s, %<level>s)` instead.'
23
+ RESTRICT_ON_SEND = %i[dirname].freeze
24
+
25
+ minimum_target_ruby_version 3.1
26
+
27
+ # @!method file_dirname?(node)
28
+ def_node_matcher :file_dirname?, <<~PATTERN
29
+ (send
30
+ (const {cbase nil?} :File) :dirname ...)
31
+ PATTERN
32
+
33
+ def on_send(node)
34
+ return if file_dirname?(node.parent) || !file_dirname?(node.first_argument)
35
+
36
+ path, level = path_with_dir_level(node, 1)
37
+ return if level < 2
38
+
39
+ message = format(MSG, path: path, level: level)
40
+ range = offense_range(node)
41
+
42
+ add_offense(range, message: message) do |corrector|
43
+ corrector.replace(range, "dirname(#{path}, #{level})")
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def path_with_dir_level(node, level)
50
+ first_argument = node.first_argument
51
+
52
+ if file_dirname?(first_argument)
53
+ level += 1
54
+ path_with_dir_level(first_argument, level)
55
+ else
56
+ [first_argument.source, level]
57
+ end
58
+ end
59
+
60
+ def offense_range(node)
61
+ range_between(node.loc.selector.begin_pos, node.source_range.end_pos)
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -54,8 +54,9 @@ module RuboCop
54
54
  private
55
55
 
56
56
  def format_message(argument)
57
- source = argument.source
58
- format(MSG, original: source, replacement: source.sub(/\s+=/, ':'))
57
+ replacement = "#{argument.name}: #{argument.default_value.source}"
58
+
59
+ format(MSG, original: argument.source, replacement: replacement)
59
60
  end
60
61
  end
61
62
  end
@@ -69,6 +69,11 @@ module RuboCop
69
69
  }
70
70
  PATTERN
71
71
 
72
+ # @!method env_const?(node)
73
+ def_node_matcher :env_const?, <<~PATTERN
74
+ (const {nil? cbase} :ENV)
75
+ PATTERN
76
+
72
77
  # @!method calls_lvar?(node, name)
73
78
  def_node_matcher :calls_lvar?, <<~PATTERN
74
79
  {
@@ -94,7 +99,7 @@ module RuboCop
94
99
  def receiver_allowed?(node)
95
100
  return false unless node
96
101
 
97
- node.hash_type? || creates_hash?(node)
102
+ node.hash_type? || creates_hash?(node) || env_const?(node)
98
103
  end
99
104
 
100
105
  def register_offense(node, block_node, regexp)
@@ -15,6 +15,11 @@ module RuboCop
15
15
  # end
16
16
  # end
17
17
  #
18
+ # # bad
19
+ # if condition_b
20
+ # do_something
21
+ # end if condition_a
22
+ #
18
23
  # # good
19
24
  # if condition_a && condition_b
20
25
  # do_something
@@ -26,12 +31,21 @@ module RuboCop
26
31
  # do_something if condition_b
27
32
  # end
28
33
  #
34
+ # # bad
35
+ # if condition_b
36
+ # do_something
37
+ # end if condition_a
38
+ #
29
39
  # @example AllowModifier: true
30
40
  # # good
31
41
  # if condition_a
32
42
  # do_something if condition_b
33
43
  # end
34
44
  #
45
+ # # good
46
+ # if condition_b
47
+ # do_something
48
+ # end if condition_a
35
49
  class SoleNestedConditional < Base
36
50
  include RangeHelp
37
51
  extend AutoCorrector
@@ -47,7 +61,7 @@ module RuboCop
47
61
 
48
62
  if_branch = node.if_branch
49
63
  return if use_variable_assignment_in_condition?(node.condition, if_branch)
50
- return unless offending_branch?(if_branch)
64
+ return unless offending_branch?(node, if_branch)
51
65
 
52
66
  message = format(MSG, conditional_type: node.keyword)
53
67
  add_offense(if_branch.loc.keyword, message: message) do |corrector|
@@ -72,13 +86,13 @@ module RuboCop
72
86
  end
73
87
  end
74
88
 
75
- def offending_branch?(branch)
89
+ def offending_branch?(node, branch)
76
90
  return false unless branch
77
91
 
78
92
  branch.if_type? &&
79
93
  !branch.else? &&
80
94
  !branch.ternary? &&
81
- !(branch.modifier_form? && allow_modifier?)
95
+ !((node.modifier_form? || branch.modifier_form?) && allow_modifier?)
82
96
  end
83
97
 
84
98
  def autocorrect(corrector, node, if_branch)
@@ -86,6 +100,14 @@ module RuboCop
86
100
  corrector.wrap(node.condition, '(', ')')
87
101
  end
88
102
 
103
+ if outer_condition_modify_form?(node, if_branch)
104
+ autocorrect_outer_condition_modify_form(corrector, node, if_branch)
105
+ else
106
+ autocorrect_outer_condition_basic(corrector, node, if_branch)
107
+ end
108
+ end
109
+
110
+ def autocorrect_outer_condition_basic(corrector, node, if_branch)
89
111
  correct_from_unless_to_if(corrector, node) if node.unless?
90
112
 
91
113
  and_operator = if_branch.unless? ? ' && !' : ' && '
@@ -97,11 +119,17 @@ module RuboCop
97
119
  end
98
120
  end
99
121
 
100
- def correct_from_unless_to_if(corrector, node)
122
+ def autocorrect_outer_condition_modify_form(corrector, node, if_branch)
123
+ correct_from_unless_to_if(corrector, if_branch, is_modify_form: true) if if_branch.unless?
124
+ correct_for_outer_condition_modify_form_style(corrector, node, if_branch)
125
+ end
126
+
127
+ def correct_from_unless_to_if(corrector, node, is_modify_form: false)
101
128
  corrector.replace(node.loc.keyword, 'if')
102
129
 
103
130
  condition = node.condition
104
- if condition.send_type? && condition.comparison_method? && !condition.parenthesized?
131
+ if (condition.send_type? && condition.comparison_method? && !condition.parenthesized?) ||
132
+ (is_modify_form && wrap_condition?(condition))
105
133
  corrector.wrap(node.condition, '!(', ')')
106
134
  else
107
135
  corrector.insert_before(node.condition, '!')
@@ -113,7 +141,7 @@ module RuboCop
113
141
  correct_outer_condition(corrector, outer_condition)
114
142
 
115
143
  condition = if_branch.condition
116
- corrector.insert_after(outer_condition, replacement_condition(and_operator, condition))
144
+ corrector.insert_after(outer_condition, "#{and_operator}#{replace_condition(condition)}")
117
145
 
118
146
  range = range_between(if_branch.loc.keyword.begin_pos, condition.source_range.end_pos)
119
147
  corrector.remove(range_with_surrounding_space(range: range, newlines: false))
@@ -129,6 +157,16 @@ module RuboCop
129
157
  corrector.wrap(if_branch.condition, '(', ')') if wrap_condition?(if_branch.condition)
130
158
  end
131
159
 
160
+ def correct_for_outer_condition_modify_form_style(corrector, node, if_branch)
161
+ condition = if_branch.condition
162
+ corrector.insert_before(condition,
163
+ "#{'!' if node.unless?}#{replace_condition(node.condition)} && ")
164
+
165
+ corrector.remove(node.condition.loc.expression)
166
+ corrector.remove(range_with_surrounding_space(range: node.loc.keyword, newlines: false))
167
+ corrector.replace(if_branch.loc.keyword, 'if')
168
+ end
169
+
132
170
  def correct_for_comment(corrector, node, if_branch)
133
171
  return if config.for_cop('Style/IfUnlessModifier')['Enabled']
134
172
 
@@ -165,17 +203,17 @@ module RuboCop
165
203
  (node.send_type? && node.arguments.any? && !node.parenthesized?)
166
204
  end
167
205
 
168
- def replacement_condition(and_operator, condition)
169
- if wrap_condition?(condition)
170
- "#{and_operator}(#{condition.source})"
171
- else
172
- "#{and_operator}#{condition.source}"
173
- end
206
+ def replace_condition(condition)
207
+ wrap_condition?(condition) ? "(#{condition.source})" : condition.source
174
208
  end
175
209
 
176
210
  def allow_modifier?
177
211
  cop_config['AllowModifier']
178
212
  end
213
+
214
+ def outer_condition_modify_form?(node, if_branch)
215
+ node.condition.loc.expression.begin_pos > if_branch.condition.loc.expression.begin_pos
216
+ end
179
217
  end
180
218
  end
181
219
  end
@@ -134,7 +134,13 @@ module RuboCop
134
134
  end
135
135
 
136
136
  def uncorrectable?(part)
137
- part.multiline? || (part.str_type? && part.heredoc?) || part.each_descendant(:block).any?
137
+ part.multiline? || heredoc?(part) || part.each_descendant(:block).any?
138
+ end
139
+
140
+ def heredoc?(node)
141
+ return false unless node.str_type? || node.dstr_type?
142
+
143
+ node.heredoc?
138
144
  end
139
145
 
140
146
  def corrected_ancestor?(node)
@@ -10,7 +10,7 @@ module RuboCop
10
10
  # last item of all non-empty, multiline array literals.
11
11
  # * `comma`: Requires a comma after last item in an array,
12
12
  # but only when each item is on its own line.
13
- # * `no_comma`: Does not requires a comma after the
13
+ # * `no_comma`: Does not require a comma after the
14
14
  # last item in an array
15
15
  #
16
16
  # @example EnforcedStyleForMultiline: consistent_comma
@@ -10,7 +10,7 @@ module RuboCop
10
10
  # last item of all non-empty, multiline hash literals.
11
11
  # * `comma`: Requires a comma after the last item in a hash,
12
12
  # but only when each item is on its own line.
13
- # * `no_comma`: Does not requires a comma after the
13
+ # * `no_comma`: Does not require a comma after the
14
14
  # last item in a hash
15
15
  #
16
16
  # @example EnforcedStyleForMultiline: consistent_comma
@@ -32,10 +32,14 @@ module RuboCop
32
32
  body_range = range_between_condition_and_else(node, node.condition)
33
33
  else_range = range_between_else_and_end(node)
34
34
 
35
+ next if part_of_ignored_node?(node)
36
+
35
37
  corrector.replace(node.loc.keyword, 'if')
36
38
  corrector.replace(body_range, else_range.source)
37
39
  corrector.replace(else_range, body_range.source)
38
40
  end
41
+
42
+ ignore_node(node)
39
43
  end
40
44
 
41
45
  def range_between_condition_and_else(node, condition)
@@ -186,11 +186,7 @@ module RuboCop
186
186
  end
187
187
 
188
188
  def regexp_captured_names(node)
189
- regexp_string = node.children.select(&:str_type?).map do |child|
190
- child.children.first
191
- end.join || ''
192
-
193
- regexp = Regexp.new(regexp_string)
189
+ regexp = node.to_regexp
194
190
 
195
191
  regexp.named_captures.keys
196
192
  end
@@ -191,8 +191,8 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
191
191
 
192
192
  def wrap_backtick(value)
193
193
  if value.is_a?(String)
194
- # Use `+` to prevent text like `**/*.gemspec` from being bold.
195
- value.start_with?('*') ? "`+#{value}+`" : "`#{value}`"
194
+ # Use `+` to prevent text like `**/*.gemspec`, `spec/**/*` from being bold.
195
+ value.include?('*') ? "`+#{value}+`" : "`#{value}`"
196
196
  else
197
197
  "`#{value}`"
198
198
  end
@@ -124,9 +124,9 @@ module RuboCop
124
124
  default_cfg = default_config(cop_name)
125
125
 
126
126
  if supports_safe_auto_correct?(cop_class, default_cfg)
127
- output_buffer.puts '# Cop supports --auto-correct.'
127
+ output_buffer.puts '# This cop supports safe auto-correction (--auto-correct).'
128
128
  elsif supports_unsafe_autocorrect?(cop_class, default_cfg)
129
- output_buffer.puts '# Cop supports --auto-correct-all.'
129
+ output_buffer.puts '# This cop supports unsafe auto-correction (--auto-correct-all).'
130
130
  end
131
131
 
132
132
  return unless default_cfg
@@ -467,8 +467,14 @@ module RuboCop
467
467
  'This option applies to the previously',
468
468
  'specified --format, or the default format',
469
469
  'if no format is specified.'],
470
- fail_level: ['Minimum severity (A/I/R/C/W/E/F) for exit',
471
- 'with error code.'],
470
+ fail_level: ['Minimum severity for exit with error code.',
471
+ ' [A] autocorrect',
472
+ ' [I] info',
473
+ ' [R] refactor',
474
+ ' [C] convention',
475
+ ' [W] warning',
476
+ ' [E] error',
477
+ ' [F] fatal'],
472
478
  display_time: 'Display elapsed time in seconds.',
473
479
  display_only_failed: ['Only output offense messages. Omit passing',
474
480
  'cops. Only valid for --format junit.'],
@@ -135,3 +135,7 @@ end
135
135
  RSpec.shared_context 'ruby 3.1', :ruby31 do
136
136
  let(:ruby_version) { 3.1 }
137
137
  end
138
+
139
+ RSpec.shared_context 'ruby 3.2', :ruby32 do
140
+ let(:ruby_version) { 3.2 }
141
+ end
@@ -8,7 +8,7 @@ module RuboCop
8
8
  class Runner # rubocop:disable Metrics/ClassLength
9
9
  # An exception indicating that the inspection loop got stuck correcting
10
10
  # offenses back and forth.
11
- class InfiniteCorrectionLoop < RuntimeError
11
+ class InfiniteCorrectionLoop < StandardError
12
12
  attr_reader :offenses
13
13
 
14
14
  def initialize(path, offenses_by_iteration, loop_start: -1)
@@ -4,7 +4,7 @@ module RuboCop
4
4
  # The kind of Ruby that code inspected by RuboCop is written in.
5
5
  # @api private
6
6
  class TargetRuby
7
- KNOWN_RUBIES = [2.5, 2.6, 2.7, 3.0, 3.1].freeze
7
+ KNOWN_RUBIES = [2.5, 2.6, 2.7, 3.0, 3.1, 3.2].freeze
8
8
  DEFAULT_VERSION = KNOWN_RUBIES.first
9
9
 
10
10
  OBSOLETE_RUBIES = {
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '1.25.1'
6
+ STRING = '1.26.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
@@ -552,6 +552,7 @@ require_relative 'rubocop/cop/style/negated_if'
552
552
  require_relative 'rubocop/cop/style/negated_if_else_condition'
553
553
  require_relative 'rubocop/cop/style/negated_unless'
554
554
  require_relative 'rubocop/cop/style/negated_while'
555
+ require_relative 'rubocop/cop/style/nested_file_dirname'
555
556
  require_relative 'rubocop/cop/style/nested_modifier'
556
557
  require_relative 'rubocop/cop/style/nested_parenthesized_calls'
557
558
  require_relative 'rubocop/cop/style/nested_ternary_operator'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.25.1
4
+ version: 1.26.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2022-02-03 00:00:00.000000000 Z
13
+ date: 2022-03-22 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: parallel
@@ -100,7 +100,7 @@ dependencies:
100
100
  requirements:
101
101
  - - ">="
102
102
  - !ruby/object:Gem::Version
103
- version: 1.15.1
103
+ version: 1.16.0
104
104
  - - "<"
105
105
  - !ruby/object:Gem::Version
106
106
  version: '2.0'
@@ -110,7 +110,7 @@ dependencies:
110
110
  requirements:
111
111
  - - ">="
112
112
  - !ruby/object:Gem::Version
113
- version: 1.15.1
113
+ version: 1.16.0
114
114
  - - "<"
115
115
  - !ruby/object:Gem::Version
116
116
  version: '2.0'
@@ -269,6 +269,7 @@ files:
269
269
  - lib/rubocop/cop/internal_affairs/node_matcher_directive.rb
270
270
  - lib/rubocop/cop/internal_affairs/node_type_predicate.rb
271
271
  - lib/rubocop/cop/internal_affairs/offense_location_keyword.rb
272
+ - lib/rubocop/cop/internal_affairs/redundant_context_config_parameter.rb
272
273
  - lib/rubocop/cop/internal_affairs/redundant_described_class_as_subject.rb
273
274
  - lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb
274
275
  - lib/rubocop/cop/internal_affairs/redundant_location_argument.rb
@@ -737,6 +738,7 @@ files:
737
738
  - lib/rubocop/cop/style/negated_if_else_condition.rb
738
739
  - lib/rubocop/cop/style/negated_unless.rb
739
740
  - lib/rubocop/cop/style/negated_while.rb
741
+ - lib/rubocop/cop/style/nested_file_dirname.rb
740
742
  - lib/rubocop/cop/style/nested_modifier.rb
741
743
  - lib/rubocop/cop/style/nested_parenthesized_calls.rb
742
744
  - lib/rubocop/cop/style/nested_ternary_operator.rb
@@ -911,7 +913,7 @@ metadata:
911
913
  homepage_uri: https://rubocop.org/
912
914
  changelog_uri: https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md
913
915
  source_code_uri: https://github.com/rubocop/rubocop/
914
- documentation_uri: https://docs.rubocop.org/rubocop/1.25/
916
+ documentation_uri: https://docs.rubocop.org/rubocop/1.26/
915
917
  bug_tracker_uri: https://github.com/rubocop/rubocop/issues
916
918
  rubygems_mfa_required: 'true'
917
919
  post_install_message: