rubocop 1.75.7 → 1.76.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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +19 -13
  3. data/config/default.yml +48 -5
  4. data/config/obsoletion.yml +6 -3
  5. data/lib/rubocop/cop/autocorrect_logic.rb +18 -10
  6. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -1
  7. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +2 -2
  8. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
  9. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +1 -0
  10. data/lib/rubocop/cop/layout/class_structure.rb +35 -0
  11. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +6 -2
  12. data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -1
  13. data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -1
  14. data/lib/rubocop/cop/lint/empty_interpolation.rb +3 -1
  15. data/lib/rubocop/cop/lint/float_comparison.rb +27 -0
  16. data/lib/rubocop/cop/lint/identity_comparison.rb +19 -15
  17. data/lib/rubocop/cop/lint/literal_as_condition.rb +16 -24
  18. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +5 -0
  19. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +87 -0
  20. data/lib/rubocop/cop/lint/useless_or.rb +98 -0
  21. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
  22. data/lib/rubocop/cop/naming/predicate_method.rb +216 -0
  23. data/lib/rubocop/cop/naming/{predicate_name.rb → predicate_prefix.rb} +2 -2
  24. data/lib/rubocop/cop/style/def_with_parentheses.rb +18 -5
  25. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +100 -0
  26. data/lib/rubocop/cop/style/if_unless_modifier.rb +2 -4
  27. data/lib/rubocop/cop/style/it_block_parameter.rb +33 -14
  28. data/lib/rubocop/cop/style/map_to_hash.rb +11 -0
  29. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +1 -1
  30. data/lib/rubocop/cop/style/redundant_array_flatten.rb +48 -0
  31. data/lib/rubocop/cop/style/redundant_format.rb +6 -1
  32. data/lib/rubocop/cop/style/redundant_parentheses.rb +16 -5
  33. data/lib/rubocop/cop/style/safe_navigation.rb +10 -7
  34. data/lib/rubocop/cop/team.rb +1 -1
  35. data/lib/rubocop/version.rb +1 -1
  36. data/lib/rubocop.rb +6 -1
  37. metadata +12 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ef87f85272e845ecc9477a7c890be9f5d8daa78ef1c9625c3252ca8bee58ee40
4
- data.tar.gz: aeb5a1576ba984944521e10cd92933cbf37080d492a25ed8e51bf46926dc0c4a
3
+ metadata.gz: 236bdf0fa5879274099388142e17b886fc852cdc1e762d50c74a31254c7c6136
4
+ data.tar.gz: 03011b196c686aa5ce1cc617d56c19041dbdc76ba237626316048be9e75dc62c
5
5
  SHA512:
6
- metadata.gz: 7f668097cd95658c24f521bed8e11b43c3b5ff01bdb49bc2e1cc5a71af630cb751a402bf8026f90cb95907fea56ff2cabe5ea0139329fa3e7fc31a46ca615fc4
7
- data.tar.gz: cb542cf88e2de605a086e69009dcb8ae2afa4d0b7a713671e6c134cf8b77e5ccd4f6e408c6566be62d57809552e7ef0de30b849b619b0192c358a1441d4060a8
6
+ metadata.gz: 91eb23282705c816206704923efdea2cb45ad09677a5d72a8741a565d9ed96146ad688aa54c1dc5be7f6d83c6e4d099605bdd91cb941d0d2b1fb36ace05b02a8
7
+ data.tar.gz: 6ff783d52e62e4dda5cae1c78f8648ef8af4078c5f052134a23c9229018e33d29a21d3ecaa80bfdc74d382b73a1a666f67d9e1b3436b1dfb5745a96861cf92ec
data/README.md CHANGED
@@ -36,10 +36,11 @@ Working on RuboCop is often fun, but it also requires a great deal of time and e
36
36
  **RuboCop**'s installation is pretty standard:
37
37
 
38
38
  ```sh
39
- $ gem install rubocop
39
+ gem install rubocop
40
40
  ```
41
41
 
42
- If you'd rather install RuboCop using `bundler`, add a line for it in your `Gemfile` (but set the `require` option to `false`, as it is a standalone tool):
42
+ If you'd rather install RuboCop using `bundler`, add a line for it in your
43
+ `Gemfile` (but set the `require` option to `false`, as it is a standalone tool):
43
44
 
44
45
  ```rb
45
46
  gem 'rubocop', require: false
@@ -52,7 +53,7 @@ To prevent an unwanted RuboCop update you might want to use a conservative versi
52
53
  in your `Gemfile`:
53
54
 
54
55
  ```rb
55
- gem 'rubocop', '~> 1.75', require: false
56
+ gem 'rubocop', '~> 1.76', require: false
56
57
  ```
57
58
 
58
59
  See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) for further details.
@@ -61,12 +62,15 @@ See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) fo
61
62
 
62
63
  Just type `rubocop` in a Ruby project's folder and watch the magic happen.
63
64
 
64
- ```
65
- $ cd my/cool/ruby/project
66
- $ rubocop
65
+ ```sh
66
+ cd my/cool/ruby/project
67
+ rubocop
67
68
  ```
68
69
 
69
- You can also use this magic in your favorite editor with RuboCop's [built-in LSP server](https://docs.rubocop.org/rubocop/usage/lsp.html).
70
+ > [!TIP]
71
+ >
72
+ > You can also use this magic in your favorite editor with RuboCop's
73
+ > [built-in LSP server](https://docs.rubocop.org/rubocop/usage/lsp.html).
70
74
 
71
75
  ## Documentation
72
76
 
@@ -79,7 +83,7 @@ RuboCop officially supports the following runtime Ruby implementations:
79
83
  * MRI 2.7+
80
84
  * JRuby 9.4+
81
85
 
82
- Targets Ruby 2.0+ code analysis.
86
+ It targets Ruby 2.0+ for code analysis.
83
87
 
84
88
  See the [compatibility documentation](https://docs.rubocop.org/rubocop/compatibility.html) for further details.
85
89
 
@@ -91,7 +95,6 @@ If you use RuboCop in your project, you can include one of these badges in your
91
95
 
92
96
  [![Ruby Style Guide](https://img.shields.io/badge/code_style-community-brightgreen.svg)](https://rubystyle.guide)
93
97
 
94
-
95
98
  Here are the Markdown snippets for the two badges:
96
99
 
97
100
  ``` markdown
@@ -157,8 +160,8 @@ wide array of funding channels to account for your preferences
157
160
  currently [Open Collective](https://opencollective.com/rubocop) is our
158
161
  preferred funding platform).
159
162
 
160
- **If you're working in a company that's making significant use of RuboCop we'd appreciate it if you suggest to your company
161
- to become a RuboCop sponsor.**
163
+ **If you're working in a company that's making significant use of RuboCop we'd
164
+ appreciate it if you suggest to your company to become a RuboCop sponsor.**
162
165
 
163
166
  You can support the development of RuboCop via
164
167
  [GitHub Sponsors](https://github.com/sponsors/bbatsov),
@@ -168,8 +171,11 @@ You can support the development of RuboCop via
168
171
  and [Tidelift](https://tidelift.com/subscription/pkg/rubygems-rubocop?utm_source=rubygems-rubocop&utm_medium=referral&utm_campaign=readme)
169
172
  .
170
173
 
171
- **Note:** If doing a sponsorship in the form of donation is problematic for your company from an accounting standpoint, we'd recommend
172
- the use of Tidelift, where you can get a support-like subscription instead.
174
+ > [!NOTE]
175
+ >
176
+ > If doing a sponsorship in the form of donation is problematic for your company
177
+ > from an accounting standpoint, we'd recommend the use of Tidelift, where you
178
+ > can get a support-like subscription instead.
173
179
 
174
180
  ### Open Collective for Individuals
175
181
 
data/config/default.yml CHANGED
@@ -1923,7 +1923,7 @@ Lint/EmptyInterpolation:
1923
1923
  Enabled: true
1924
1924
  AutoCorrect: contextual
1925
1925
  VersionAdded: '0.20'
1926
- VersionChanged: '1.61'
1926
+ VersionChanged: '1.76'
1927
1927
 
1928
1928
  Lint/EmptyWhen:
1929
1929
  Description: 'Checks for `when` branches with empty bodies.'
@@ -2423,8 +2423,9 @@ Lint/ShadowingOuterLocalVariable:
2423
2423
  Description: >-
2424
2424
  Do not use the same name as outer local variable
2425
2425
  for block arguments or block local variables.
2426
- Enabled: true
2426
+ Enabled: false
2427
2427
  VersionAdded: '0.9'
2428
+ VersionChanged: '1.76'
2428
2429
 
2429
2430
  Lint/SharedMutableDefault:
2430
2431
  Description: 'Checks for mutable literals used as default arguments during Hash initialization.'
@@ -2612,6 +2613,13 @@ Lint/UselessConstantScoping:
2612
2613
  Enabled: pending
2613
2614
  VersionAdded: '1.72'
2614
2615
 
2616
+ Lint/UselessDefaultValueArgument:
2617
+ Description: 'Checks for usage of `fetch` or `Array.new` with default value argument and block.'
2618
+ Enabled: pending
2619
+ VersionAdded: '1.76'
2620
+ Safe: false
2621
+ AllowedReceivers: []
2622
+
2615
2623
  Lint/UselessDefined:
2616
2624
  Description: 'Checks for calls to `defined?` with strings and symbols. The result of such a call will always be truthy.'
2617
2625
  Enabled: pending
@@ -2636,6 +2644,11 @@ Lint/UselessNumericOperation:
2636
2644
  Enabled: pending
2637
2645
  VersionAdded: '1.66'
2638
2646
 
2647
+ Lint/UselessOr:
2648
+ Description: 'Checks for useless OR expressions.'
2649
+ Enabled: pending
2650
+ VersionAdded: '1.76'
2651
+
2639
2652
  Lint/UselessRescue:
2640
2653
  Description: 'Checks for useless `rescue`s.'
2641
2654
  Enabled: pending
@@ -3046,8 +3059,20 @@ Naming/MethodParameterName:
3046
3059
  # Forbidden names that will register an offense
3047
3060
  ForbiddenNames: []
3048
3061
 
3049
- Naming/PredicateName:
3050
- Description: 'Check the names of predicate methods.'
3062
+ Naming/PredicateMethod:
3063
+ Description: 'Checks that predicate methods end with `?` and non-predicate methods do not.'
3064
+ Enabled: pending
3065
+ VersionAdded: '1.76'
3066
+ # In `aggressive` mode, the cop will register an offense for predicate methods that
3067
+ # may return a non-boolean value.
3068
+ # In `conservative` mode, the cop will *not* register an offense for predicate methods
3069
+ # that may return a non-boolean value.
3070
+ Mode: conservative
3071
+ AllowedMethods:
3072
+ - call
3073
+
3074
+ Naming/PredicatePrefix:
3075
+ Description: 'Predicate method names should not be prefixed and end with a `?`.'
3051
3076
  StyleGuide: '#bool-methods-qmark'
3052
3077
  Enabled: true
3053
3078
  VersionAdded: '0.50'
@@ -3939,6 +3964,16 @@ Style/EmptyMethod:
3939
3964
  - compact
3940
3965
  - expanded
3941
3966
 
3967
+ Style/EmptyStringInsideInterpolation:
3968
+ Description: 'Checks for empty strings being assigned inside string interpolation.'
3969
+ StyleGuide: '#empty-strings-in-interpolation'
3970
+ Enabled: pending
3971
+ EnforcedStyle: trailing_conditional
3972
+ SupportedStyles:
3973
+ - trailing_conditional
3974
+ - ternary
3975
+ VersionAdded: '1.76'
3976
+
3942
3977
  Style/Encoding:
3943
3978
  Description: 'Use UTF-8 as the source file encoding.'
3944
3979
  StyleGuide: '#utf-8'
@@ -4433,12 +4468,14 @@ Style/ItAssignment:
4433
4468
  Style/ItBlockParameter:
4434
4469
  Description: 'Checks for blocks with one argument where `it` block parameter can be used.'
4435
4470
  Enabled: pending
4436
- EnforcedStyle: only_numbered_parameters
4471
+ EnforcedStyle: allow_single_line
4437
4472
  SupportedStyles:
4473
+ - allow_single_line
4438
4474
  - only_numbered_parameters
4439
4475
  - always
4440
4476
  - disallow
4441
4477
  VersionAdded: '1.75'
4478
+ VersionChanged: '1.76'
4442
4479
 
4443
4480
  Style/KeywordArgumentsMerging:
4444
4481
  Description: >-
@@ -5151,6 +5188,12 @@ Style/RedundantArrayConstructor:
5151
5188
  Enabled: pending
5152
5189
  VersionAdded: '1.52'
5153
5190
 
5191
+ Style/RedundantArrayFlatten:
5192
+ Description: 'Checks for redundant calls of `Array#flatten`.'
5193
+ Enabled: pending
5194
+ Safe: false
5195
+ VersionAdded: '1.76'
5196
+
5154
5197
  Style/RedundantAssignment:
5155
5198
  Description: 'Checks for redundant assignment before returning.'
5156
5199
  Enabled: true
@@ -31,6 +31,9 @@ renamed:
31
31
  Lint/UnneededRequireStatement: Lint/RedundantRequireStatement
32
32
  Lint/UnneededSplatExpansion: Lint/RedundantSplatExpansion
33
33
  Metrics/LineLength: Layout/LineLength
34
+ Naming/PredicateName:
35
+ new_name: Naming/PredicatePrefix
36
+ severity: warning
34
37
  Naming/UncommunicativeBlockParamName: Naming/BlockParameterName
35
38
  Naming/UncommunicativeMethodParamName: Naming/MethodParameterName
36
39
  Style/AccessorMethodName: Naming/AccessorMethodName
@@ -44,7 +47,7 @@ renamed:
44
47
  Style/MethodName: Naming/MethodName
45
48
  Style/OpMethod: Naming/BinaryOperatorParameterName
46
49
  Style/PredicateName:
47
- new_name: Naming/PredicateName
50
+ new_name: Naming/PredicatePrefix
48
51
  severity: warning
49
52
  Style/SingleSpaceBeforeFirstArg: Layout/SpaceBeforeFirstArg
50
53
  Style/UnneededCapitalW: Style/RedundantCapitalW
@@ -179,10 +182,10 @@ changed_parameters:
179
182
  - cops: Naming/HeredocDelimiterNaming
180
183
  parameters: Blacklist
181
184
  alternative: ForbiddenDelimiters
182
- - cops: Naming/PredicateName
185
+ - cops: Naming/PredicatePrefix
183
186
  parameters: NamePrefixBlacklist
184
187
  alternative: ForbiddenPrefixes
185
- - cops: Naming/PredicateName
188
+ - cops: Naming/PredicatePrefix
186
189
  parameters: NameWhitelist
187
190
  alternative: AllowedMethods
188
191
  - cops:
@@ -50,7 +50,7 @@ module RuboCop
50
50
 
51
51
  def disable_offense(offense_range)
52
52
  unbreakable_range = multiline_ranges(offense_range)&.find do |range|
53
- range_overlaps_offense?(offense_range, range)
53
+ eol_comment_would_be_inside_literal?(offense_range, range)
54
54
  end
55
55
 
56
56
  if unbreakable_range
@@ -75,18 +75,22 @@ module RuboCop
75
75
  end
76
76
 
77
77
  def disable_offense_with_eol_or_surround_comment(range)
78
- eol_comment = " # rubocop:todo #{cop_name}"
79
- needed_line_length = (range.source_line + eol_comment).length
80
-
81
- if needed_line_length <= max_line_length
82
- disable_offense_at_end_of_line(range_of_first_line(range), eol_comment)
83
- else
78
+ if line_with_eol_comment_too_long?(range)
84
79
  disable_offense_before_and_after(range_by_lines(range))
80
+ else
81
+ disable_offense_at_end_of_line(range_of_first_line(range))
85
82
  end
86
83
  end
87
84
 
88
- def range_overlaps_offense?(offense_range, range)
89
- offense_range.begin_pos > range.begin_pos && range.overlaps?(offense_range)
85
+ def eol_comment_would_be_inside_literal?(offense_range, literal_range)
86
+ return true if line_with_eol_comment_too_long?(offense_range)
87
+
88
+ offense_line = offense_range.line
89
+ offense_line >= literal_range.first_line && offense_line < literal_range.last_line
90
+ end
91
+
92
+ def line_with_eol_comment_too_long?(range)
93
+ (range.source_line + eol_comment).length > max_line_length
90
94
  end
91
95
 
92
96
  def surrounding_heredoc?(node)
@@ -132,10 +136,14 @@ module RuboCop
132
136
  config.for_cop('Layout/LineLength')['Max'] || 120
133
137
  end
134
138
 
135
- def disable_offense_at_end_of_line(range, eol_comment)
139
+ def disable_offense_at_end_of_line(range)
136
140
  Corrector.new(range).insert_after(range, eol_comment)
137
141
  end
138
142
 
143
+ def eol_comment
144
+ " # rubocop:todo #{cop_name}"
145
+ end
146
+
139
147
  def disable_offense_before_and_after(range_by_lines)
140
148
  range_with_newline = range_by_lines.resize(range_by_lines.size + 1)
141
149
  leading_whitespace = range_by_lines.source_line[/^\s*/]
@@ -45,7 +45,7 @@ module RuboCop
45
45
 
46
46
  gem_declarations(processed_source.ast)
47
47
  .each_cons(2) do |previous, current|
48
- next unless consecutive_lines(previous, current)
48
+ next unless consecutive_lines?(previous, current)
49
49
  next unless case_insensitive_out_of_order?(gem_name(current), gem_name(previous))
50
50
 
51
51
  register_offense(previous, current)
@@ -57,13 +57,13 @@ module RuboCop
57
57
  # @!method assignment_method_declarations(node)
58
58
  def_node_search :assignment_method_declarations, <<~PATTERN
59
59
  (send
60
- (lvar #match_block_variable_name?) _ ...)
60
+ (lvar {#match_block_variable_name? :_1 :it}) _ ...)
61
61
  PATTERN
62
62
 
63
63
  # @!method indexed_assignment_method_declarations(node)
64
64
  def_node_search :indexed_assignment_method_declarations, <<~PATTERN
65
65
  (send
66
- (send (lvar #match_block_variable_name?) _)
66
+ (send (lvar {#match_block_variable_name? :_1 :it}) _)
67
67
  :[]=
68
68
  literal?
69
69
  _
@@ -71,7 +71,7 @@ module RuboCop
71
71
 
72
72
  dependency_declarations(processed_source.ast)
73
73
  .each_cons(2) do |previous, current|
74
- next unless consecutive_lines(previous, current)
74
+ next unless consecutive_lines?(previous, current)
75
75
  next unless case_insensitive_out_of_order?(gem_name(current), gem_name(previous))
76
76
  next unless get_dependency_name(previous) == get_dependency_name(current)
77
77
 
@@ -29,6 +29,7 @@ module RuboCop
29
29
  NODE_GROUPS = {
30
30
  any_block: %i[block numblock itblock],
31
31
  any_def: %i[def defs],
32
+ any_match_pattern: %i[match_pattern match_pattern_p],
32
33
  argument: %i[arg optarg restarg kwarg kwoptarg kwrestarg blockarg forward_arg shadowarg],
33
34
  boolean: %i[true false],
34
35
  call: %i[send csend],
@@ -22,6 +22,11 @@ module RuboCop
22
22
  # * Private attribute macros (`attr_accessor`, `attr_writer`, `attr_reader`)
23
23
  # * Private instance methods
24
24
  #
25
+ # NOTE: Simply enabling the cop with `Enabled: true` will not use
26
+ # the example order shown below.
27
+ # To enforce the order of macros like `attr_reader`,
28
+ # you must define both `ExpectedOrder` *and* `Categories`.
29
+ #
25
30
  # You can configure the following order:
26
31
  #
27
32
  # [source,yaml]
@@ -68,6 +73,36 @@ module RuboCop
68
73
  # - extend
69
74
  # ----
70
75
  #
76
+ # If you only set `ExpectedOrder`
77
+ # without defining `Categories`,
78
+ # macros such as `attr_reader` or `has_many`
79
+ # will not be recognized as part of a category, and their order will not be validated.
80
+ # For example, the following will NOT raise any offenses, even if the order is incorrect:
81
+ #
82
+ # [source,yaml]
83
+ # ----
84
+ # Layout/ClassStructure:
85
+ # Enabled: true
86
+ # ExpectedOrder:
87
+ # - public_attribute_macros
88
+ # - initializer
89
+ # ----
90
+ #
91
+ # To make it work as expected, you must also specify `Categories` like this:
92
+ #
93
+ # [source,yaml]
94
+ # ----
95
+ # Layout/ClassStructure:
96
+ # ExpectedOrder:
97
+ # - public_attribute_macros
98
+ # - initializer
99
+ # Categories:
100
+ # attribute_macros:
101
+ # - attr_reader
102
+ # - attr_writer
103
+ # - attr_accessor
104
+ # ----
105
+ #
71
106
  # @safety
72
107
  # Autocorrection is unsafe because class methods and module inclusion
73
108
  # can behave differently, based on which methods or constants have
@@ -116,7 +116,7 @@ module RuboCop
116
116
  def allowed_only_before_style?(node)
117
117
  if node.special_modifier?
118
118
  return true if processed_source[node.last_line] == 'end'
119
- return false if next_line_empty?(node.last_line)
119
+ return false if next_line_empty_and_exists?(node.last_line)
120
120
  end
121
121
 
122
122
  previous_line_empty?(node.first_line)
@@ -129,7 +129,7 @@ module RuboCop
129
129
  when :around
130
130
  corrector.insert_after(line, "\n") unless next_line_empty?(node.last_line)
131
131
  when :only_before
132
- if next_line_empty?(node.last_line)
132
+ if next_line_empty_and_exists?(node.last_line)
133
133
  range = next_empty_line_range(node)
134
134
 
135
135
  corrector.remove(range)
@@ -154,6 +154,10 @@ module RuboCop
154
154
  body_end?(last_send_line) || next_line.blank?
155
155
  end
156
156
 
157
+ def next_line_empty_and_exists?(last_send_line)
158
+ next_line_empty?(last_send_line) && last_send_line.next != processed_source.lines.size
159
+ end
160
+
157
161
  def empty_lines_around?(node)
158
162
  previous_line_empty?(node.first_line) && next_line_empty?(node.last_line)
159
163
  end
@@ -155,7 +155,7 @@ module RuboCop
155
155
  def on_send(node)
156
156
  return unless should_check?(node)
157
157
  return if same_line?(node, node.first_argument)
158
- return if style != :consistent && enforce_first_argument_with_fixed_indentation? &&
158
+ return if enforce_first_argument_with_fixed_indentation? &&
159
159
  !enable_layout_first_method_argument_line_break?
160
160
 
161
161
  indent = base_indentation(node) + configured_indentation_width
@@ -132,7 +132,7 @@ module RuboCop
132
132
  def_node_matcher :delegate_method?, <<~PATTERN
133
133
  (send nil? :delegate
134
134
  ({sym str} $_)+
135
- (hash <(pair (sym :to) _) ...>)
135
+ (hash <(pair (sym :to) {sym str}) ...>)
136
136
  )
137
137
  PATTERN
138
138
 
@@ -19,7 +19,9 @@ module RuboCop
19
19
  MSG = 'Empty interpolation detected.'
20
20
 
21
21
  def on_interpolation(begin_node)
22
- return unless begin_node.children.empty?
22
+ node_children = begin_node.children.dup
23
+ node_children.delete_if { |e| e&.nil_type? || (e&.basic_literal? && e&.value&.empty?) }
24
+ return unless node_children.empty?
23
25
 
24
26
  add_offense(begin_node) { |corrector| corrector.remove(begin_node) }
25
27
  end
@@ -15,6 +15,14 @@ module RuboCop
15
15
  # x == 0.1
16
16
  # x != 0.1
17
17
  #
18
+ # # bad
19
+ # case value
20
+ # when 1.0
21
+ # foo
22
+ # when 2.0
23
+ # bar
24
+ # end
25
+ #
18
26
  # # good - using BigDecimal
19
27
  # x.to_d == 0.1.to_d
20
28
  #
@@ -32,12 +40,21 @@ module RuboCop
32
40
  # # good - comparing against nil
33
41
  # Float(x, exception: false) == nil
34
42
  #
43
+ # # good - using epsilon comparison in case expression
44
+ # case
45
+ # when (value - 1.0).abs < Float::EPSILON
46
+ # foo
47
+ # when (value - 2.0).abs < Float::EPSILON
48
+ # bar
49
+ # end
50
+ #
35
51
  # # Or some other epsilon based type of comparison:
36
52
  # # https://www.embeddeduse.com/2019/08/26/qt-compare-two-floats/
37
53
  #
38
54
  class FloatComparison < Base
39
55
  MSG_EQUALITY = 'Avoid equality comparisons of floats as they are unreliable.'
40
56
  MSG_INEQUALITY = 'Avoid inequality comparisons of floats as they are unreliable.'
57
+ MSG_CASE = 'Avoid float literal comparisons in case statements as they are unreliable.'
41
58
 
42
59
  EQUALITY_METHODS = %i[== != eql? equal?].freeze
43
60
  FLOAT_RETURNING_METHODS = %i[to_f Float fdiv].freeze
@@ -58,6 +75,16 @@ module RuboCop
58
75
  end
59
76
  alias on_csend on_send
60
77
 
78
+ def on_case(node)
79
+ node.when_branches.each do |when_branch|
80
+ when_branch.each_condition do |condition|
81
+ next if !float?(condition) || literal_safe?(condition)
82
+
83
+ add_offense(condition, message: MSG_CASE)
84
+ end
85
+ end
86
+ end
87
+
61
88
  private
62
89
 
63
90
  def float?(node)
@@ -11,39 +11,43 @@ module RuboCop
11
11
  # @example
12
12
  # # bad
13
13
  # foo.object_id == bar.object_id
14
+ # foo.object_id != baz.object_id
14
15
  #
15
16
  # # good
16
17
  # foo.equal?(bar)
18
+ # !foo.equal?(baz)
17
19
  #
18
20
  class IdentityComparison < Base
19
21
  extend AutoCorrector
20
22
 
21
- MSG = 'Use `equal?` instead `==` when comparing `object_id`.'
22
- RESTRICT_ON_SEND = %i[==].freeze
23
+ MSG = 'Use `%<bang>sequal?` instead of `%<comparison_method>s` when comparing `object_id`.'
24
+ RESTRICT_ON_SEND = %i[== !=].freeze
25
+
26
+ # @!method object_id_comparison(node)
27
+ def_node_matcher :object_id_comparison, <<~PATTERN
28
+ (send
29
+ (send
30
+ _lhs_receiver :object_id) ${:== :!=}
31
+ (send
32
+ _rhs_receiver :object_id))
33
+ PATTERN
23
34
 
24
35
  def on_send(node)
25
- return unless compare_between_object_id_by_double_equal?(node)
36
+ return unless (comparison_method = object_id_comparison(node))
26
37
 
27
- add_offense(node) do |corrector|
38
+ bang = comparison_method == :== ? '' : '!'
39
+ add_offense(node,
40
+ message: format(MSG, comparison_method: comparison_method,
41
+ bang: bang)) do |corrector|
28
42
  receiver = node.receiver.receiver
29
43
  argument = node.first_argument.receiver
30
44
  return unless receiver && argument
31
45
 
32
- replacement = "#{receiver.source}.equal?(#{argument.source})"
46
+ replacement = "#{bang}#{receiver.source}.equal?(#{argument.source})"
33
47
 
34
48
  corrector.replace(node, replacement)
35
49
  end
36
50
  end
37
-
38
- private
39
-
40
- def compare_between_object_id_by_double_equal?(node)
41
- object_id_method?(node.receiver) && object_id_method?(node.first_argument)
42
- end
43
-
44
- def object_id_method?(node)
45
- node.send_type? && node.method?(:object_id)
46
- end
47
51
  end
48
52
  end
49
53
  end
@@ -240,30 +240,22 @@ module RuboCop
240
240
  def correct_if_node(node, cond)
241
241
  result = condition_evaluation(node, cond)
242
242
 
243
- if node.elsif? && result
244
- add_offense(cond) do |corrector|
245
- corrector.replace(node, "else\n #{node.if_branch.source}")
246
- end
247
- elsif node.elsif? && !result
248
- add_offense(cond) do |corrector|
249
- corrector.replace(node, "else\n #{node.else_branch.source}")
250
- end
251
- elsif node.if_branch && result
252
- add_offense(cond) do |corrector|
253
- corrector.replace(node, node.if_branch.source)
254
- end
255
- elsif node.elsif_conditional?
256
- add_offense(cond) do |corrector|
257
- corrector.replace(node, "#{node.else_branch.source.sub('elsif', 'if')}\nend")
258
- end
259
- elsif node.else? || node.ternary?
260
- add_offense(cond) do |corrector|
261
- corrector.replace(node, node.else_branch.source)
262
- end
263
- else
264
- add_offense(cond) do |corrector|
265
- corrector.remove(node)
266
- end
243
+ new_node = if node.elsif? && result
244
+ "else\n #{range_with_comments(node.if_branch).source}"
245
+ elsif node.elsif? && !result
246
+ "else\n #{node.else_branch.source}"
247
+ elsif node.if_branch && result
248
+ node.if_branch.source
249
+ elsif node.elsif_conditional?
250
+ "#{node.else_branch.source.sub('elsif', 'if')}\nend"
251
+ elsif node.else? || node.ternary?
252
+ node.else_branch.source
253
+ else
254
+ '' # Equivalent to removing the node
255
+ end
256
+
257
+ add_offense(cond) do |corrector|
258
+ corrector.replace(node, new_node)
267
259
  end
268
260
  end
269
261
  # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
@@ -8,6 +8,11 @@ module RuboCop
8
8
  # given by `ruby -cw` prior to Ruby 2.6:
9
9
  # "shadowing outer local variable - foo".
10
10
  #
11
+ # The cop is now disabled by default to match the upstream Ruby behavior.
12
+ # It's useful, however, if you'd like to avoid shadowing variables from outer
13
+ # scopes, which some people consider an anti-pattern that makes it harder
14
+ # to keep track of what's going on in a program.
15
+ #
11
16
  # NOTE: Shadowing of variables in block passed to `Ractor.new` is allowed
12
17
  # because `Ractor` should not access outer variables.
13
18
  # eg. following style is encouraged: