rubocop 1.75.8 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 31a9b16f521cb4dd8d22db58d5473cd527162b65934d8e210993f7c435a85d26
4
- data.tar.gz: 57af5692e8b85aca41224e621a0ba6b54001944a6625edef169a78278066a532
3
+ metadata.gz: 236bdf0fa5879274099388142e17b886fc852cdc1e762d50c74a31254c7c6136
4
+ data.tar.gz: 03011b196c686aa5ce1cc617d56c19041dbdc76ba237626316048be9e75dc62c
5
5
  SHA512:
6
- metadata.gz: ef0ab376b3993e9ad1961560911ed5b5e2abf069a2b9acd0187e3d31c03f853a520646f2c421813bc3c03aedd74004dcf057cbadecad0ea86acafbd6670c8e46
7
- data.tar.gz: b76bd8b4d7492577429bab8abd02ebcf7f2d9d2f83066e81c48b060abcaad68a2660b064c1f5b0b9c42bc80cc78ce15cd6e10ace393cd636310055515af925db
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:
@@ -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],
@@ -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
@@ -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:
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # Checks for usage of method `fetch` or `Array.new` with default value argument
7
+ # and block. In such cases, block will always be used as default value.
8
+ #
9
+ # This cop emulates Ruby warning "block supersedes default value argument" which
10
+ # applies to `Array.new`, `Array#fetch`, `Hash#fetch`, `ENV.fetch` and
11
+ # `Thread#fetch`.
12
+ #
13
+ # @safety
14
+ # This cop is unsafe because the receiver could have nonstandard implementation
15
+ # of `fetch`, or be a class other than the one listed above.
16
+ #
17
+ # It is also unsafe because default value argument could have side effects:
18
+ #
19
+ # [source,ruby]
20
+ # ----
21
+ # def x(a) = puts "side effect"
22
+ # Array.new(5, x(1)) { 2 }
23
+ # ----
24
+ #
25
+ # so removing it would change behavior.
26
+ #
27
+ # @example
28
+ # # bad
29
+ # x.fetch(key, default_value) { block_value }
30
+ # Array.new(size, default_value) { block_value }
31
+ #
32
+ # # good
33
+ # x.fetch(key) { block_value }
34
+ # Array.new(size) { block_value }
35
+ #
36
+ # # also good - in case default value argument is desired instead
37
+ # x.fetch(key, default_value)
38
+ # Array.new(size, default_value)
39
+ #
40
+ # # good - keyword arguments aren't registered as offenses
41
+ # x.fetch(key, keyword: :arg) { block_value }
42
+ #
43
+ # @example AllowedReceivers: ['Rails.cache']
44
+ # # good
45
+ # Rails.cache.fetch(name, options) { block }
46
+ #
47
+ class UselessDefaultValueArgument < Base
48
+ include AllowedReceivers
49
+ extend AutoCorrector
50
+
51
+ MSG = 'Block supersedes default value argument.'
52
+
53
+ RESTRICT_ON_SEND = %i[fetch new].freeze
54
+
55
+ # @!method default_value_argument_and_block(node)
56
+ def_node_matcher :default_value_argument_and_block, <<~PATTERN
57
+ (any_block
58
+ {
59
+ (call _receiver :fetch $_key $_default_value)
60
+ (send (const _ :Array) :new $_size $_default_value)
61
+ }
62
+ _args
63
+ _block_body)
64
+ PATTERN
65
+
66
+ def on_send(node)
67
+ unless (prev_arg_node, default_value_node = default_value_argument_and_block(node.parent))
68
+ return
69
+ end
70
+ return if allowed_receiver?(node.receiver)
71
+ return if hash_without_braces?(default_value_node)
72
+
73
+ add_offense(default_value_node) do |corrector|
74
+ corrector.remove(prev_arg_node.source_range.end.join(default_value_node.source_range))
75
+ end
76
+ end
77
+ alias on_csend on_send
78
+
79
+ private
80
+
81
+ def hash_without_braces?(node)
82
+ node.hash_type? && !node.braces?
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # Checks for useless OR (`||` and `or`) expressions.
7
+ #
8
+ # Some methods always return a truthy value, even when called
9
+ # on `nil` (e.g. `nil.to_i` evaluates to `0`). Therefore, OR expressions
10
+ # appended after these methods will never evaluate.
11
+ #
12
+ # @example
13
+ #
14
+ # # bad
15
+ # x.to_a || fallback
16
+ # x.to_c || fallback
17
+ # x.to_d || fallback
18
+ # x.to_i || fallback
19
+ # x.to_f || fallback
20
+ # x.to_h || fallback
21
+ # x.to_r || fallback
22
+ # x.to_s || fallback
23
+ # x.to_sym || fallback
24
+ # x.intern || fallback
25
+ # x.inspect || fallback
26
+ # x.hash || fallback
27
+ # x.object_id || fallback
28
+ # x.__id__ || fallback
29
+ #
30
+ # x.to_s or fallback
31
+ #
32
+ # # good - if fallback is same as return value of method called on nil
33
+ # x.to_a # nil.to_a returns []
34
+ # x.to_c # nil.to_c returns (0+0i)
35
+ # x.to_d # nil.to_d returns 0.0
36
+ # x.to_i # nil.to_i returns 0
37
+ # x.to_f # nil.to_f returns 0.0
38
+ # x.to_h # nil.to_h returns {}
39
+ # x.to_r # nil.to_r returns (0/1)
40
+ # x.to_s # nil.to_s returns ''
41
+ # x.to_sym # nil.to_sym raises an error
42
+ # x.intern # nil.intern raises an error
43
+ # x.inspect # nil.inspect returns "nil"
44
+ # x.hash # nil.hash returns an Integer
45
+ # x.object_id # nil.object_id returns an Integer
46
+ # x.__id__ # nil.object_id returns an Integer
47
+ #
48
+ # # good - if the intention is not to call the method on nil
49
+ # x&.to_a || fallback
50
+ # x&.to_c || fallback
51
+ # x&.to_d || fallback
52
+ # x&.to_i || fallback
53
+ # x&.to_f || fallback
54
+ # x&.to_h || fallback
55
+ # x&.to_r || fallback
56
+ # x&.to_s || fallback
57
+ # x&.to_sym || fallback
58
+ # x&.intern || fallback
59
+ # x&.inspect || fallback
60
+ # x&.hash || fallback
61
+ # x&.object_id || fallback
62
+ # x&.__id__ || fallback
63
+ #
64
+ # x&.to_s or fallback
65
+ #
66
+ class UselessOr < Base
67
+ MSG = '`%<rhs>s` will never evaluate because `%<lhs>s` always returns a truthy value.'
68
+
69
+ TRUTHY_RETURN_VALUE_METHODS = Set[:to_a, :to_c, :to_d, :to_i, :to_f, :to_h, :to_r,
70
+ :to_s, :to_sym, :intern, :inspect, :hash, :object_id,
71
+ :__id__].freeze
72
+
73
+ # @!method truthy_return_value_method?(node)
74
+ def_node_matcher :truthy_return_value_method?, <<~PATTERN
75
+ (send _ %TRUTHY_RETURN_VALUE_METHODS)
76
+ PATTERN
77
+
78
+ def on_or(node)
79
+ if truthy_return_value_method?(node.lhs)
80
+ report_offense(node, node.lhs)
81
+ elsif truthy_return_value_method?(node.rhs)
82
+ parent = node.parent
83
+ parent = parent.parent if parent&.begin_type?
84
+
85
+ report_offense(parent, node.rhs) if parent&.or_type?
86
+ end
87
+ end
88
+
89
+ private
90
+
91
+ def report_offense(or_node, truthy_node)
92
+ add_offense(or_node.loc.operator.join(or_node.rhs.source_range),
93
+ message: format(MSG, lhs: truthy_node.source, rhs: or_node.rhs.source))
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -24,7 +24,7 @@ module RuboCop
24
24
  gem_canonical_name(string_a) < gem_canonical_name(string_b)
25
25
  end
26
26
 
27
- def consecutive_lines(previous, current)
27
+ def consecutive_lines?(previous, current)
28
28
  first_line = get_source_range(current, treat_comments_as_separators).first_line
29
29
  previous.source_range.last_line == first_line - 1
30
30
  end