rubocop 1.73.2 → 1.74.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.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +31 -2
  4. data/lib/rubocop/config_loader_resolver.rb +2 -1
  5. data/lib/rubocop/config_obsoletion/extracted_cop.rb +4 -3
  6. data/lib/rubocop/config_obsoletion.rb +1 -1
  7. data/lib/rubocop/cop/lint/literal_as_condition.rb +4 -0
  8. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +2 -2
  9. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +9 -3
  10. data/lib/rubocop/cop/lint/return_in_void_context.rb +4 -11
  11. data/lib/rubocop/cop/lint/shared_mutable_default.rb +12 -1
  12. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +2 -11
  13. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +1 -1
  14. data/lib/rubocop/cop/mixin/target_ruby_version.rb +1 -1
  15. data/lib/rubocop/cop/style/class_and_module_children.rb +29 -7
  16. data/lib/rubocop/cop/style/commented_keyword.rb +9 -2
  17. data/lib/rubocop/cop/style/comparable_between.rb +75 -0
  18. data/lib/rubocop/cop/style/double_negation.rb +1 -1
  19. data/lib/rubocop/cop/style/exponential_notation.rb +2 -2
  20. data/lib/rubocop/cop/style/format_string_token.rb +38 -11
  21. data/lib/rubocop/cop/style/if_unless_modifier.rb +2 -2
  22. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +3 -3
  23. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +14 -4
  24. data/lib/rubocop/cop/style/rescue_modifier.rb +3 -0
  25. data/lib/rubocop/directive_comment.rb +1 -1
  26. data/lib/rubocop/ext/regexp_node.rb +0 -1
  27. data/lib/rubocop/version.rb +1 -1
  28. data/lib/rubocop.rb +1 -0
  29. metadata +5 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 671bf05708f62eb3abe3b448168f7daa905cffe60e2652ad4ec21964594adf70
4
- data.tar.gz: caef12b9b307560cda7125652fa0249fcc0908725e97cc963a10f4c1e0782faa
3
+ metadata.gz: 440bee0e28f294bab2eba4c11e9c681342917c69ad4c00e36eceb88743767c29
4
+ data.tar.gz: 68e9e209a22e891a38b677f97344d2d10e9a8913dd999751472f301deed43b31
5
5
  SHA512:
6
- metadata.gz: fea1053c5a5f72ffc9eea331b438f1fc1d876eed21c0f251569ab541cf3f9724193e1c3bf72a0154e84608ac4346cb7a122fcc4a5266fe7b520109c56c5dea1a
7
- data.tar.gz: e2b4c854a638b119a3b28e39e77dd05d86fe85b890180bc9a70ac10727f9e74e1ed64e659fe397042610e4bc87b3153d5783d472a596de110628395708da6177
6
+ metadata.gz: 70db27a5b7a0e00696672a5b4fcd4e3bf35aa40c85ef65ccb630914ab110a5e5b657927aa1b1c8637be76b7041a90e2ad3c4ed188110266c048d0c4d6a4be0b4
7
+ data.tar.gz: e5599d30a4f776d85d202acad2ac03c7e4568e64a1c4038cb87b8ee3372b2428882924a91ea49853c98cf0614280d002918363eef41fe0990dc8b1895d8e9776
data/README.md CHANGED
@@ -52,7 +52,7 @@ To prevent an unwanted RuboCop update you might want to use a conservative versi
52
52
  in your `Gemfile`:
53
53
 
54
54
  ```rb
55
- gem 'rubocop', '~> 1.73', require: false
55
+ gem 'rubocop', '~> 1.74', require: false
56
56
  ```
57
57
 
58
58
  See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) for further details.
data/config/default.yml CHANGED
@@ -3104,6 +3104,8 @@ Naming/VariableNumber:
3104
3104
  CheckMethodNames: true
3105
3105
  CheckSymbols: true
3106
3106
  AllowedIdentifiers:
3107
+ - TLS1_1 # OpenSSL::SSL::TLS1_1_VERSION
3108
+ - TLS1_2 # OpenSSL::SSL::TLS1_2_VERSION
3107
3109
  - capture3 # Open3.capture3
3108
3110
  - iso8601 # Time#iso8601
3109
3111
  - rfc1123_date # CGI.rfc1123_date
@@ -3499,6 +3501,7 @@ Style/ClassAndModuleChildren:
3499
3501
  SafeAutoCorrect: false
3500
3502
  Enabled: true
3501
3503
  VersionAdded: '0.19'
3504
+ VersionChanged: '1.74'
3502
3505
  #
3503
3506
  # Basically there are two different styles:
3504
3507
  #
@@ -3514,7 +3517,21 @@ Style/ClassAndModuleChildren:
3514
3517
  #
3515
3518
  # The compact style is only forced, for classes or modules with one child.
3516
3519
  EnforcedStyle: nested
3517
- SupportedStyles:
3520
+ SupportedStyles: &supported_styles
3521
+ - nested
3522
+ - compact
3523
+ # Configure classes separately, if desired. If not set, or set to `nil`,
3524
+ # the `EnforcedStyle` value will be used.
3525
+ EnforcedStyleForClasses: ~
3526
+ SupportedStylesForClasses:
3527
+ - ~
3528
+ - nested
3529
+ - compact
3530
+ # Configure modules separately, if desired. If not set, or set to `nil`,
3531
+ # the `EnforcedStyle` value will be used.
3532
+ EnforcedStyleForModules: ~
3533
+ SupportedStylesForModules:
3534
+ - ~
3518
3535
  - nested
3519
3536
  - compact
3520
3537
 
@@ -3667,6 +3684,12 @@ Style/CommentedKeyword:
3667
3684
  VersionAdded: '0.51'
3668
3685
  VersionChanged: '1.19'
3669
3686
 
3687
+ Style/ComparableBetween:
3688
+ Description: 'Enforces the use of `Comparable#between?` instead of logical comparison.'
3689
+ Enabled: pending
3690
+ VersionAdded: '1.74'
3691
+ StyleGuide: '#ranges-or-between'
3692
+
3670
3693
  Style/ComparableClamp:
3671
3694
  Description: 'Enforces the use of `Comparable#clamp` instead of comparison by minimum and maximum.'
3672
3695
  Enabled: pending
@@ -4069,8 +4092,14 @@ Style/FormatStringToken:
4069
4092
  # style token in a format string to be allowed when enforced style is not
4070
4093
  # `unannotated`.
4071
4094
  MaxUnannotatedPlaceholdersAllowed: 1
4095
+ # The mode the cop operates in. Two values are allowed:
4096
+ # * aggressive (default): all strings are considered
4097
+ # * conservative:
4098
+ # only register offenses for strings given to `printf`, `sprintf`,
4099
+ # format` and `%` methods. Other strings are not considered.
4100
+ Mode: aggressive
4072
4101
  VersionAdded: '0.49'
4073
- VersionChanged: '1.0'
4102
+ VersionChanged: '1.74'
4074
4103
  AllowedMethods: []
4075
4104
  AllowedPatterns: []
4076
4105
 
@@ -9,7 +9,8 @@ module RuboCop
9
9
  # @api private
10
10
  class ConfigLoaderResolver # rubocop:disable Metrics/ClassLength
11
11
  def resolve_plugins(rubocop_config, plugins)
12
- return if (plugins = Array(plugins)).empty?
12
+ plugins = Array(plugins) - ConfigLoader.loaded_plugins.map { |plugin| plugin.about.name }
13
+ return if plugins.empty?
13
14
 
14
15
  Plugin.integrate_plugins(rubocop_config, plugins)
15
16
  end
@@ -15,7 +15,7 @@ module RuboCop
15
15
  end
16
16
 
17
17
  def violated?
18
- return false if feature_loaded?
18
+ return false if plugin_loaded?
19
19
 
20
20
  affected_cops.any?
21
21
  end
@@ -38,8 +38,9 @@ module RuboCop
38
38
  end
39
39
  end
40
40
 
41
- def feature_loaded?
42
- config.loaded_features.include?(gem)
41
+ def plugin_loaded?
42
+ # Plugins loaded via `require` are included in `loaded_features`.
43
+ config.loaded_plugins.include?(gem) || config.loaded_features.include?(gem)
43
44
  end
44
45
  end
45
46
  end
@@ -50,7 +50,7 @@ module RuboCop
50
50
  # Default rules for obsoletions are in config/obsoletion.yml
51
51
  # Additional rules files can be added with `RuboCop::ConfigObsoletion.files << filename`
52
52
  def load_rules # rubocop:disable Metrics/AbcSize
53
- rules = LOAD_RULES_CACHE[self.class.files] ||=
53
+ rules = LOAD_RULES_CACHE[self.class.files.hash] ||=
54
54
  self.class.files.each_with_object({}) do |filename, hash|
55
55
  hash.merge!(YAML.safe_load(File.read(filename)) || {}) do |_key, first, second|
56
56
  case first
@@ -46,6 +46,10 @@ module RuboCop
46
46
  return unless node.lhs.truthy_literal?
47
47
 
48
48
  add_offense(node.lhs) do |corrector|
49
+ # Don't autocorrect `'foo' && return` because having `return` as
50
+ # the leftmost node can lead to a void value expression syntax error.
51
+ next if node.rhs.type?(:return, :break, :next)
52
+
49
53
  corrector.replace(node, node.rhs.source)
50
54
  end
51
55
  end
@@ -46,7 +46,7 @@ module RuboCop
46
46
  def on_return(return_node)
47
47
  return if return_value?(return_node)
48
48
 
49
- return_node.each_ancestor(:block, :def, :defs) do |node|
49
+ return_node.each_ancestor(:any_block, :def, :defs) do |node|
50
50
  break if scoped_node?(node)
51
51
 
52
52
  # if a proc is passed to `Module#define_method` or
@@ -54,7 +54,7 @@ module RuboCop
54
54
  # non-local exit error
55
55
  break if define_method?(node.send_node)
56
56
 
57
- next unless node.arguments?
57
+ next if node.argument_list.empty?
58
58
 
59
59
  if chained_send?(node.send_node)
60
60
  add_offense(return_node.loc.keyword)
@@ -3,13 +3,13 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # Checks for redundant uses of `to_s`, `to_sym`, `to_i`, `to_f`, `to_r`, `to_c`,
6
+ # Checks for redundant uses of `to_s`, `to_sym`, `to_i`, `to_f`, `to_d`, `to_r`, `to_c`,
7
7
  # `to_a`, `to_h`, and `to_set`.
8
8
  #
9
9
  # When one of these methods is called on an object of the same type, that object
10
10
  # is returned, making the call unnecessary. The cop detects conversion methods called
11
11
  # on object literals, class constructors, class `[]` methods, and the `Kernel` methods
12
- # `String()`, `Integer()`, `Float()`, `Rational()`, `Complex()` and `Array()`.
12
+ # `String()`, `Integer()`, `Float()`, BigDecimal(), `Rational()`, `Complex()`, and `Array()`.
13
13
  #
14
14
  # Specifically, these cases are detected for each conversion method:
15
15
  #
@@ -98,6 +98,7 @@ module RuboCop
98
98
  to_s: 'string_constructor?',
99
99
  to_i: 'integer_constructor?',
100
100
  to_f: 'float_constructor?',
101
+ to_d: 'bigdecimal_constructor?',
101
102
  to_r: 'rational_constructor?',
102
103
  to_c: 'complex_constructor?',
103
104
  to_a: 'array_constructor?',
@@ -110,7 +111,7 @@ module RuboCop
110
111
  TYPED_METHODS = { to_s: %i[inspect] }.freeze
111
112
 
112
113
  CONVERSION_METHODS = Set[*LITERAL_NODE_TYPES.keys].freeze
113
- RESTRICT_ON_SEND = CONVERSION_METHODS
114
+ RESTRICT_ON_SEND = CONVERSION_METHODS + [:to_d]
114
115
 
115
116
  private_constant :LITERAL_NODE_TYPES, :CONSTRUCTOR_MAPPING
116
117
 
@@ -137,6 +138,11 @@ module RuboCop
137
138
  #type_constructor?(:Float)
138
139
  PATTERN
139
140
 
141
+ # @!method bigdecimal_constructor?(node)
142
+ def_node_matcher :bigdecimal_constructor?, <<~PATTERN
143
+ #type_constructor?(:BigDecimal)
144
+ PATTERN
145
+
140
146
  # @!method rational_constructor?(node)
141
147
  def_node_matcher :rational_constructor?, <<~PATTERN
142
148
  #type_constructor?(:Rational)
@@ -35,22 +35,15 @@ module RuboCop
35
35
  def on_return(return_node)
36
36
  return unless return_node.descendants.any?
37
37
 
38
- context_node = non_void_context(return_node)
39
-
40
- return unless context_node&.def_type?
41
- return unless context_node&.void_context?
38
+ def_node = return_node.each_ancestor(:def).first
39
+ return unless def_node&.void_context?
40
+ return if return_node.each_ancestor(:any_block).any?(&:lambda?)
42
41
 
43
42
  add_offense(
44
43
  return_node.loc.keyword,
45
- message: format(message, method: context_node.method_name)
44
+ message: format(message, method: def_node.method_name)
46
45
  )
47
46
  end
48
-
49
- private
50
-
51
- def non_void_context(return_node)
52
- return_node.each_ancestor(:block, :def, :defs).first
53
- end
54
47
  end
55
48
  end
56
49
  end
@@ -51,7 +51,18 @@ module RuboCop
51
51
 
52
52
  # @!method hash_initialized_with_mutable_shared_object?(node)
53
53
  def_node_matcher :hash_initialized_with_mutable_shared_object?, <<~PATTERN
54
- (send (const {nil? cbase} :Hash) :new {array hash (send (const {nil? cbase} {:Array :Hash}) :new)})
54
+ {
55
+ (send (const {nil? cbase} :Hash) :new [
56
+ {array hash (send (const {nil? cbase} {:Array :Hash}) :new)}
57
+ !#capacity_keyword_argument?
58
+ ])
59
+ (send (const {nil? cbase} :Hash) :new hash #capacity_keyword_argument?)
60
+ }
61
+ PATTERN
62
+
63
+ # @!method capacity_keyword_argument?(node)
64
+ def_node_matcher :capacity_keyword_argument?, <<~PATTERN
65
+ (hash (pair (sym :capacity) _))
55
66
  PATTERN
56
67
 
57
68
  def on_send(node)
@@ -4,8 +4,8 @@ module RuboCop
4
4
  module Cop
5
5
  module Lint
6
6
  # Checks for useless constant scoping. Private constants must be defined using
7
- # `private_constant` or `class << self`. Even if `private` access modifier is used,
8
- # it is public scope despite its appearance.
7
+ # `private_constant`. Even if `private` access modifier is used, it is public scope despite
8
+ # its appearance.
9
9
  #
10
10
  # It does not support autocorrection due to behavior change and multiple ways to fix it.
11
11
  # Or a public constant may be intended.
@@ -26,14 +26,6 @@ module RuboCop
26
26
  #
27
27
  # # good
28
28
  # class Foo
29
- # class << self
30
- # private
31
- # PRIVATE_CONST = 42
32
- # end
33
- # end
34
- #
35
- # # good
36
- # class Foo
37
29
  # PUBLIC_CONST = 42 # If private scope is not intended.
38
30
  # end
39
31
  #
@@ -46,7 +38,6 @@ module RuboCop
46
38
  PATTERN
47
39
 
48
40
  def on_casgn(node)
49
- return if node.each_ancestor(:sclass).any?
50
41
  return unless after_private_modifier?(node.left_siblings)
51
42
  return if private_constantize?(node.right_siblings, node.name)
52
43
 
@@ -35,7 +35,7 @@ module RuboCop
35
35
  comment_line_numbers = processed_source.comments.map { |comment| comment.loc.line }
36
36
 
37
37
  comment_line_numbers.any? do |comment_line_number|
38
- comment_line_number >= node.first_line && comment_line_number <= node.last_line
38
+ comment_line_number.between?(node.first_line, node.last_line)
39
39
  end
40
40
  end
41
41
 
@@ -29,7 +29,7 @@ module RuboCop
29
29
  min = required_minimum_ruby_version || 0
30
30
  max = required_maximum_ruby_version || Float::INFINITY
31
31
 
32
- min <= version && max >= version
32
+ version.between?(min, max)
33
33
  end
34
34
  end
35
35
  end
@@ -3,14 +3,26 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks the style of children definitions at classes and
7
- # modules. Basically there are two different styles:
6
+ # Checks that namespaced classes and modules are defined with a consistent style.
7
+ #
8
+ # With `nested` style, classes and modules should be defined separately (one constant
9
+ # on each line, without `::`). With `compact` style, classes and modules should be
10
+ # defined with fully qualified names (using `::` for namespaces).
11
+ #
12
+ # NOTE: The style chosen will affect `Module.nesting` for the class or module. Using
13
+ # `nested` style will result in each level being added, whereas `compact` style will
14
+ # only include the fully qualified class or module name.
15
+ #
16
+ # By default, `EnforcedStyle` applies to both classes and modules. If desired, separate
17
+ # styles can be defined for classes and modules by using `EnforcedStyleForClasses` and
18
+ # `EnforcedStyleForModules` respectively. If not set, or set to nil, the `EnforcedStyle`
19
+ # value will be used.
8
20
  #
9
21
  # @safety
10
22
  # Autocorrection is unsafe.
11
23
  #
12
- # Moving from compact to nested children requires knowledge of whether the
13
- # outer parent is a module or a class. Moving from nested to compact requires
24
+ # Moving from `compact` to `nested` children requires knowledge of whether the
25
+ # outer parent is a module or a class. Moving from `nested` to `compact` requires
14
26
  # verification that the outer parent is defined elsewhere. RuboCop does not
15
27
  # have the knowledge to perform either operation safely and thus requires
16
28
  # manual oversight.
@@ -42,16 +54,18 @@ module RuboCop
42
54
  def on_class(node)
43
55
  return if node.parent_class && style != :nested
44
56
 
45
- check_style(node, node.body)
57
+ check_style(node, node.body, style_for_classes)
46
58
  end
47
59
 
48
60
  def on_module(node)
49
- check_style(node, node.body)
61
+ check_style(node, node.body, style_for_modules)
50
62
  end
51
63
 
52
64
  private
53
65
 
54
66
  def nest_or_compact(corrector, node)
67
+ style = node.class_type? ? style_for_classes : style_for_modules
68
+
55
69
  if style == :nested
56
70
  nest_definition(corrector, node)
57
71
  else
@@ -141,7 +155,7 @@ module RuboCop
141
155
  node.source_range.source_line[/\A\s*/]
142
156
  end
143
157
 
144
- def check_style(node, body)
158
+ def check_style(node, body, style)
145
159
  return if node.identifier.namespace&.cbase_type?
146
160
 
147
161
  if style == :nested
@@ -183,6 +197,14 @@ module RuboCop
183
197
  def compact_node_name?(node)
184
198
  node.identifier.source.include?('::')
185
199
  end
200
+
201
+ def style_for_classes
202
+ cop_config['EnforcedStyleForClasses'] || style
203
+ end
204
+
205
+ def style_for_modules
206
+ cop_config['EnforcedStyleForModules'] || style
207
+ end
186
208
  end
187
209
  end
188
210
  end
@@ -9,8 +9,8 @@ module RuboCop
9
9
  # These keywords are: `class`, `module`, `def`, `begin`, `end`.
10
10
  #
11
11
  # Note that some comments
12
- # (`:nodoc:`, `:yields:`, `rubocop:disable` and `rubocop:todo`)
13
- # and RBS::Inline annotation comments are allowed.
12
+ # (`:nodoc:`, `:yields:`, `rubocop:disable` and `rubocop:todo`),
13
+ # RBS::Inline annotation, and Steep annotation (`steep:ignore`) are allowed.
14
14
  #
15
15
  # Autocorrection removes comments from `end` keyword and keeps comments
16
16
  # for `class`, `module`, `def` and `begin` above the keyword.
@@ -60,6 +60,8 @@ module RuboCop
60
60
  SUBCLASS_DEFINITION = /\A\s*class\s+(\w|::)+\s*<\s*(\w|::)+/.freeze
61
61
  METHOD_DEFINITION = /\A\s*def\s/.freeze
62
62
 
63
+ STEEP_REGEXP = /#\ssteep:ignore(\s|\z)/.freeze
64
+
63
65
  def on_new_investigation
64
66
  processed_source.comments.each do |comment|
65
67
  next unless offensive?(comment) && (match = source_line(comment).match(REGEXP))
@@ -86,6 +88,7 @@ module RuboCop
86
88
  def offensive?(comment)
87
89
  line = source_line(comment)
88
90
  return false if rbs_inline_annotation?(line, comment)
91
+ return false if steep_annotation?(comment)
89
92
 
90
93
  KEYWORD_REGEXES.any? { |r| r.match?(line) } &&
91
94
  ALLOWED_COMMENT_REGEXES.none? { |r| r.match?(line) }
@@ -105,6 +108,10 @@ module RuboCop
105
108
  false
106
109
  end
107
110
  end
111
+
112
+ def steep_annotation?(comment)
113
+ comment.text.match?(STEEP_REGEXP)
114
+ end
108
115
  end
109
116
  end
110
117
  end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for logical comparison which can be replaced with `Comparable#between?`.
7
+ #
8
+ # NOTE: `Comparable#between?` is on average slightly slower than logical comparison,
9
+ # although the difference generally isn't observable. If you require maximum
10
+ # performance, consider using logical comparison.
11
+ #
12
+ # @example
13
+ #
14
+ # # bad
15
+ # x >= min && x <= max
16
+ #
17
+ # # bad
18
+ # x <= max && x >= min
19
+ #
20
+ # # good
21
+ # x.between?(min, max)
22
+ #
23
+ class ComparableBetween < Base
24
+ extend AutoCorrector
25
+
26
+ MSG = 'Prefer `%<prefer>s` over logical comparison.'
27
+
28
+ # @!method logical_comparison_between_by_min_first?(node)
29
+ def_node_matcher :logical_comparison_between_by_min_first?, <<~PATTERN
30
+ (and
31
+ (send
32
+ {$_value :>= $_min | $_min :<= $_value})
33
+ (send
34
+ {$_value :<= $_max | $_max :>= $_value}))
35
+ PATTERN
36
+
37
+ # @!method logical_comparison_between_by_max_first?(node)
38
+ def_node_matcher :logical_comparison_between_by_max_first?, <<~PATTERN
39
+ (and
40
+ (send
41
+ {$_value :<= $_max | $_max :>= $_value})
42
+ (send
43
+ {$_value :>= $_min | $_min :<= $_value}))
44
+ PATTERN
45
+
46
+ def on_and(node)
47
+ logical_comparison_between_by_min_first?(node) do |*args|
48
+ min_and_value, max_and_value = args.each_slice(2).to_a
49
+
50
+ register_offense(node, min_and_value, max_and_value)
51
+ end
52
+
53
+ logical_comparison_between_by_max_first?(node) do |*args|
54
+ max_and_value, min_and_value = args.each_slice(2).to_a
55
+
56
+ register_offense(node, min_and_value, max_and_value)
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def register_offense(node, min_and_value, max_and_value)
63
+ value = (min_and_value & max_and_value).first
64
+ min = min_and_value.find { _1 != value }
65
+ max = max_and_value.find { _1 != value }
66
+
67
+ prefer = "#{value.source}.between?(#{min.source}, #{max.source})"
68
+ add_offense(node, message: format(MSG, prefer: prefer)) do |corrector|
69
+ corrector.replace(node, prefer)
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -109,7 +109,7 @@ module RuboCop
109
109
  end
110
110
 
111
111
  def define_method?(node)
112
- return false unless node.block_type?
112
+ return false unless node.any_block_type?
113
113
 
114
114
  child = node.child_nodes.first
115
115
  return false unless child.send_type?
@@ -60,8 +60,8 @@ module RuboCop
60
60
  class ExponentialNotation < Base
61
61
  include ConfigurableEnforcedStyle
62
62
  MESSAGES = {
63
- scientific: 'Use a mantissa in [1, 10[.',
64
- engineering: 'Use an exponent divisible by 3 and a mantissa in [0.1, 1000[.',
63
+ scientific: 'Use a mantissa >= 1 and < 10.',
64
+ engineering: 'Use an exponent divisible by 3 and a mantissa >= 0.1 and < 1000.',
65
65
  integral: 'Use an integer as mantissa, without trailing zero.'
66
66
  }.freeze
67
67
 
@@ -3,16 +3,24 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Use a consistent style for named format string tokens.
6
+ # Use a consistent style for tokens within a format string.
7
7
  #
8
- # NOTE: `unannotated` style cop only works for strings
9
- # which are passed as arguments to those methods:
10
- # `printf`, `sprintf`, `format`, `%`.
11
- # The reason is that _unannotated_ format is very similar
12
- # to encoded URLs or Date/Time formatting strings.
8
+ # By default, all strings are evaluated. In some cases, this may be undesirable,
9
+ # as they could be used as arguments to a method that does not consider
10
+ # them to be tokens, but rather other identifiers or just part of the string.
13
11
  #
14
- # This cop's allowed methods can be customized with `AllowedMethods`.
15
- # By default, there are no allowed methods.
12
+ # `AllowedMethods` or `AllowedPatterns` can be configured with in order to mark specific
13
+ # methods as always allowed, thereby avoiding an offense from the cop. By default, there
14
+ # are no allowed methods.
15
+ #
16
+ # Additionally, the cop can be made conservative by configuring it with
17
+ # `Mode: conservative` (default `aggressive`). In this mode, tokens (regardless
18
+ # of `EnforcedStyle`) are only considered if used in the format string argument to the
19
+ # methods `printf`, `sprintf`, `format` and `%`.
20
+ #
21
+ # NOTE: Tokens in the `unannotated` style (eg. `%s`) are always treated as if
22
+ # configured with `Conservative: true`. This is done in order to prevent false positives,
23
+ # because this format is very similar to encoded URLs or Date/Time formatting strings.
16
24
  #
17
25
  # @example EnforcedStyle: annotated (default)
18
26
  #
@@ -82,6 +90,20 @@ module RuboCop
82
90
  # # good
83
91
  # redirect('foo/%{bar_id}')
84
92
  #
93
+ # @example Mode: conservative, EnforcedStyle: annotated
94
+ # # In `conservative` mode, offenses are only registered for strings
95
+ # # given to a known formatting method.
96
+ #
97
+ # # good
98
+ # "%{greeting}"
99
+ # foo("%{greeting}")
100
+ #
101
+ # # bad
102
+ # format("%{greeting}", greeting: 'Hello')
103
+ # printf("%{greeting}", greeting: 'Hello')
104
+ # sprintf("%{greeting}", greeting: 'Hello')
105
+ # "%{greeting}" % { greeting: 'Hello' }
106
+ #
85
107
  class FormatStringToken < Base
86
108
  include ConfigurableEnforcedStyle
87
109
  include AllowedMethods
@@ -153,8 +175,9 @@ module RuboCop
153
175
  corrector.replace(token_range, correction)
154
176
  end
155
177
 
156
- def unannotated_format?(node, detected_style)
157
- detected_style == :unannotated && !format_string_in_typical_context?(node)
178
+ def allowed_string?(node, detected_style)
179
+ (detected_style == :unannotated || conservative?) &&
180
+ !format_string_in_typical_context?(node)
158
181
  end
159
182
 
160
183
  def message(detected_style)
@@ -203,7 +226,7 @@ module RuboCop
203
226
  def collect_detections(node)
204
227
  detections = []
205
228
  tokens(node) do |detected_sequence, token_range|
206
- unless unannotated_format?(node, detected_sequence.style)
229
+ unless allowed_string?(node, detected_sequence.style)
207
230
  detections << [detected_sequence, token_range]
208
231
  end
209
232
  end
@@ -222,6 +245,10 @@ module RuboCop
222
245
  def max_unannotated_placeholders_allowed
223
246
  cop_config['MaxUnannotatedPlaceholdersAllowed']
224
247
  end
248
+
249
+ def conservative?
250
+ cop_config.fetch('Mode', :aggressive).to_sym == :conservative
251
+ end
225
252
  end
226
253
  end
227
254
  end
@@ -164,8 +164,8 @@ module RuboCop
164
164
 
165
165
  def too_long_due_to_comment_after_modifier?(node, comment)
166
166
  source_length = processed_source.lines[node.first_line - 1].length
167
- source_length >= max_line_length &&
168
- source_length - comment.source_range.length <= max_line_length
167
+
168
+ max_line_length.between?(source_length - comment.source_range.length, source_length)
169
169
  end
170
170
 
171
171
  def allowed_patterns
@@ -108,7 +108,7 @@ module RuboCop
108
108
  end
109
109
 
110
110
  def call_in_literals?(node)
111
- parent = node.parent&.block_type? ? node.parent.parent : node.parent
111
+ parent = node.parent&.any_block_type? ? node.parent.parent : node.parent
112
112
  return false unless parent
113
113
 
114
114
  parent.type?(:pair, :array, :range) ||
@@ -117,7 +117,7 @@ module RuboCop
117
117
  end
118
118
 
119
119
  def call_in_logical_operators?(node)
120
- parent = node.parent&.block_type? ? node.parent.parent : node.parent
120
+ parent = node.parent&.any_block_type? ? node.parent.parent : node.parent
121
121
  return false unless parent
122
122
 
123
123
  logical_operator?(parent) ||
@@ -153,7 +153,7 @@ module RuboCop
153
153
  end
154
154
 
155
155
  def call_in_argument_with_block?(node)
156
- parent = node.parent&.block_type? && node.parent.parent
156
+ parent = node.parent&.any_block_type? && node.parent.parent
157
157
  return false unless parent
158
158
 
159
159
  parent.type?(:call, :super, :yield)
@@ -20,20 +20,30 @@ module RuboCop
20
20
 
21
21
  MSG = 'Remove the redundant current directory path.'
22
22
  RESTRICT_ON_SEND = %i[require_relative].freeze
23
- CURRENT_DIRECTORY_PATH = './'
23
+ CURRENT_DIRECTORY_PREFIX = %r{./+}.freeze
24
+ REDUNDANT_CURRENT_DIRECTORY_PREFIX = /\A#{CURRENT_DIRECTORY_PREFIX}/.freeze
24
25
 
25
26
  def on_send(node)
26
27
  return unless (first_argument = node.first_argument)
27
- return unless first_argument.str_content&.start_with?(CURRENT_DIRECTORY_PATH)
28
- return unless (index = first_argument.source.index(CURRENT_DIRECTORY_PATH))
28
+ return unless (index = first_argument.source.index(CURRENT_DIRECTORY_PREFIX))
29
+ return unless (redundant_length = redundant_path_length(first_argument.str_content))
29
30
 
30
31
  begin_pos = first_argument.source_range.begin.begin_pos + index
31
- range = range_between(begin_pos, begin_pos + 2)
32
+ end_pos = begin_pos + redundant_length
33
+ range = range_between(begin_pos, end_pos)
32
34
 
33
35
  add_offense(range) do |corrector|
34
36
  corrector.remove(range)
35
37
  end
36
38
  end
39
+
40
+ private
41
+
42
+ def redundant_path_length(path)
43
+ return unless (match = path&.match(REDUNDANT_CURRENT_DIRECTORY_PREFIX))
44
+
45
+ match[0].length
46
+ end
37
47
  end
38
48
  end
39
49
  end
@@ -67,11 +67,13 @@ module RuboCop
67
67
  node.parent && parentheses?(node.parent)
68
68
  end
69
69
 
70
+ # rubocop:disable Metrics/AbcSize
70
71
  def correct_rescue_block(corrector, node, parenthesized)
71
72
  operation = node.body
72
73
 
73
74
  node_indentation, node_offset = indentation_and_offset(node, parenthesized)
74
75
 
76
+ corrector.wrap(operation, '[', ']') if operation.array_type? && !operation.bracketed?
75
77
  corrector.remove(range_between(operation.source_range.end_pos, node.source_range.end_pos))
76
78
  corrector.insert_before(operation, "begin\n#{node_indentation}")
77
79
  corrector.insert_after(heredoc_end(operation) || operation, <<~RESCUE_CLAUSE.chop)
@@ -81,6 +83,7 @@ module RuboCop
81
83
  #{node_offset}end
82
84
  RESCUE_CLAUSE
83
85
  end
86
+ # rubocop:enable Metrics/AbcSize
84
87
 
85
88
  def indentation_and_offset(node, parenthesized)
86
89
  node_indentation = indentation(node)
@@ -12,7 +12,7 @@ module RuboCop
12
12
  # @api private
13
13
  LINT_SYNTAX_COP = "#{LINT_DEPARTMENT}/Syntax"
14
14
  # @api private
15
- COP_NAME_PATTERN = '([A-Z]\w+/)*(?:[A-Z]\w+)'
15
+ COP_NAME_PATTERN = '([A-Za-z]\w+/)*(?:[A-Za-z]\w+)'
16
16
  # @api private
17
17
  COP_NAMES_PATTERN = "(?:#{COP_NAME_PATTERN} , )*#{COP_NAME_PATTERN}"
18
18
  # @api private
@@ -43,7 +43,6 @@ module RuboCop
43
43
  def named_capturing?(exp, event, named)
44
44
  event == :enter &&
45
45
  named == exp.respond_to?(:name) &&
46
- !exp.text.start_with?('(?<=') &&
47
46
  exp.respond_to?(:capturing?) &&
48
47
  exp.capturing?
49
48
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '1.73.2'
6
+ STRING = '1.74.0'
7
7
 
8
8
  MSG = '%<version>s (using %<parser_version>s, ' \
9
9
  'rubocop-ast %<rubocop_ast_version>s, ' \
data/lib/rubocop.rb CHANGED
@@ -509,6 +509,7 @@ require_relative 'rubocop/cop/style/combinable_loops'
509
509
  require_relative 'rubocop/cop/style/command_literal'
510
510
  require_relative 'rubocop/cop/style/comment_annotation'
511
511
  require_relative 'rubocop/cop/style/commented_keyword'
512
+ require_relative 'rubocop/cop/style/comparable_between'
512
513
  require_relative 'rubocop/cop/style/comparable_clamp'
513
514
  require_relative 'rubocop/cop/style/concat_array_literals'
514
515
  require_relative 'rubocop/cop/style/conditional_assignment'
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.73.2
4
+ version: 1.74.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
@@ -9,7 +9,7 @@ authors:
9
9
  - Yuji Nakayama
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2025-03-04 00:00:00.000000000 Z
12
+ date: 2025-03-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: json
@@ -729,6 +729,7 @@ files:
729
729
  - lib/rubocop/cop/style/command_literal.rb
730
730
  - lib/rubocop/cop/style/comment_annotation.rb
731
731
  - lib/rubocop/cop/style/commented_keyword.rb
732
+ - lib/rubocop/cop/style/comparable_between.rb
732
733
  - lib/rubocop/cop/style/comparable_clamp.rb
733
734
  - lib/rubocop/cop/style/concat_array_literals.rb
734
735
  - lib/rubocop/cop/style/conditional_assignment.rb
@@ -1075,9 +1076,9 @@ licenses:
1075
1076
  - MIT
1076
1077
  metadata:
1077
1078
  homepage_uri: https://rubocop.org/
1078
- changelog_uri: https://github.com/rubocop/rubocop/releases/tag/v1.73.2
1079
+ changelog_uri: https://github.com/rubocop/rubocop/releases/tag/v1.74.0
1079
1080
  source_code_uri: https://github.com/rubocop/rubocop/
1080
- documentation_uri: https://docs.rubocop.org/rubocop/1.73/
1081
+ documentation_uri: https://docs.rubocop.org/rubocop/1.74/
1081
1082
  bug_tracker_uri: https://github.com/rubocop/rubocop/issues
1082
1083
  rubygems_mfa_required: 'true'
1083
1084
  rdoc_options: []