rubocop 1.76.0 → 1.77.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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/config/default.yml +24 -2
  4. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +5 -2
  5. data/lib/rubocop/cop/gemspec/attribute_assignment.rb +91 -0
  6. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +0 -22
  7. data/lib/rubocop/cop/gemspec/require_mfa.rb +15 -1
  8. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +4 -4
  9. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +1 -1
  10. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -1
  11. data/lib/rubocop/cop/layout/line_length.rb +26 -5
  12. data/lib/rubocop/cop/layout/space_before_brackets.rb +2 -9
  13. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +7 -2
  14. data/lib/rubocop/cop/lint/ambiguous_range.rb +5 -0
  15. data/lib/rubocop/cop/lint/empty_interpolation.rb +1 -1
  16. data/lib/rubocop/cop/lint/float_comparison.rb +4 -4
  17. data/lib/rubocop/cop/lint/literal_as_condition.rb +2 -2
  18. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
  19. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +4 -4
  20. data/lib/rubocop/cop/lint/self_assignment.rb +25 -0
  21. data/lib/rubocop/cop/lint/useless_access_modifier.rb +29 -4
  22. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +4 -1
  23. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +3 -3
  24. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  25. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -1
  26. data/lib/rubocop/cop/mixin/gemspec_help.rb +22 -0
  27. data/lib/rubocop/cop/mixin/line_length_help.rb +24 -8
  28. data/lib/rubocop/cop/naming/file_name.rb +2 -2
  29. data/lib/rubocop/cop/naming/predicate_method.rb +74 -9
  30. data/lib/rubocop/cop/naming/predicate_prefix.rb +2 -2
  31. data/lib/rubocop/cop/style/case_like_if.rb +1 -1
  32. data/lib/rubocop/cop/style/collection_querying.rb +167 -0
  33. data/lib/rubocop/cop/style/conditional_assignment.rb +3 -1
  34. data/lib/rubocop/cop/style/exponential_notation.rb +2 -2
  35. data/lib/rubocop/cop/style/fetch_env_var.rb +32 -6
  36. data/lib/rubocop/cop/style/hash_conversion.rb +12 -3
  37. data/lib/rubocop/cop/style/if_unless_modifier.rb +11 -2
  38. data/lib/rubocop/cop/style/it_block_parameter.rb +2 -2
  39. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +1 -1
  40. data/lib/rubocop/cop/style/min_max_comparison.rb +13 -5
  41. data/lib/rubocop/cop/style/redundant_array_flatten.rb +3 -1
  42. data/lib/rubocop/cop/style/redundant_interpolation.rb +1 -1
  43. data/lib/rubocop/cop/style/redundant_parentheses.rb +12 -2
  44. data/lib/rubocop/cop/style/redundant_self.rb +8 -5
  45. data/lib/rubocop/cop/style/safe_navigation.rb +14 -4
  46. data/lib/rubocop/cop/style/sole_nested_conditional.rb +2 -1
  47. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  48. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  49. data/lib/rubocop/formatter/fuubar_style_formatter.rb +1 -1
  50. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  51. data/lib/rubocop/lsp/diagnostic.rb +4 -4
  52. data/lib/rubocop/rspec/expect_offense.rb +9 -3
  53. data/lib/rubocop/version.rb +1 -1
  54. data/lib/rubocop.rb +2 -0
  55. data/lib/ruby_lsp/rubocop/addon.rb +2 -2
  56. metadata +8 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 236bdf0fa5879274099388142e17b886fc852cdc1e762d50c74a31254c7c6136
4
- data.tar.gz: 03011b196c686aa5ce1cc617d56c19041dbdc76ba237626316048be9e75dc62c
3
+ metadata.gz: 871a632c96e6a5c92e57b651037eaf7efd7412e64ac77440d5275027ef791d4e
4
+ data.tar.gz: f375ec6479b1f90193cd39e863411707d60cd071e170888005339d89a19ea65b
5
5
  SHA512:
6
- metadata.gz: 91eb23282705c816206704923efdea2cb45ad09677a5d72a8741a565d9ed96146ad688aa54c1dc5be7f6d83c6e4d099605bdd91cb941d0d2b1fb36ace05b02a8
7
- data.tar.gz: 6ff783d52e62e4dda5cae1c78f8648ef8af4078c5f052134a23c9229018e33d29a21d3ecaa80bfdc74d382b73a1a666f67d9e1b3436b1dfb5745a96861cf92ec
6
+ metadata.gz: f8dac3eb5cb12741c110c8726ec64595cefb89700527144cc590d7febb676164e3902dff171594278f7895d1c3df336dea3e14f869d285a0c7080b1467bd6362
7
+ data.tar.gz: 4aadc7a712774810e1815f4f31bea43fc6d9e121a547621f57894c7df848a6459dfc962af0fb725f4fef6e2017b6a499c203cc05a4d2879d0e50aceaee156b95
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.76', require: false
56
+ gem 'rubocop', '~> 1.77', require: false
57
57
  ```
58
58
 
59
59
  See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) for further details.
@@ -112,7 +112,7 @@ Here's a list of RuboCop's core developers:
112
112
  * [Yuji Nakayama](https://github.com/yujinakayama) (retired)
113
113
  * [Evgeni Dzhelyov](https://github.com/edzhelyov) (retired)
114
114
  * [Ted Johansson](https://github.com/drenmi)
115
- * [Masataka Kuwabara](https://github.com/pocke)
115
+ * [Masataka Kuwabara](https://github.com/pocke) (retired)
116
116
  * [Koichi Ito](https://github.com/koic)
117
117
  * [Maxim Krizhanovski](https://github.com/darhazer)
118
118
  * [Benjamin Quorning](https://github.com/bquorning)
data/config/default.yml CHANGED
@@ -281,6 +281,13 @@ Gemspec/AddRuntimeDependency:
281
281
  Include:
282
282
  - '**/*.gemspec'
283
283
 
284
+ Gemspec/AttributeAssignment:
285
+ Description: 'Use consistent style for Gemspec attributes assignment.'
286
+ Enabled: pending
287
+ VersionAdded: '1.77'
288
+ Include:
289
+ - '**/*.gemspec'
290
+
284
291
  Gemspec/DependencyVersion:
285
292
  Description: 'Requires or forbids specifying gem dependency versions.'
286
293
  Enabled: false
@@ -1095,6 +1102,7 @@ Layout/LineLength:
1095
1102
  # To make it possible to copy or click on URIs in the code, we allow lines
1096
1103
  # containing a URI to be longer than Max.
1097
1104
  AllowURI: true
1105
+ AllowQualifiedName: true
1098
1106
  URISchemes:
1099
1107
  - http
1100
1108
  - https
@@ -2400,6 +2408,7 @@ Lint/SelfAssignment:
2400
2408
  Description: 'Checks for self-assignments.'
2401
2409
  Enabled: true
2402
2410
  VersionAdded: '0.89'
2411
+ AllowRBSInlineAnnotation: false
2403
2412
 
2404
2413
  Lint/SendWithMixinArgument:
2405
2414
  Description: 'Checks for `send` method when using mixin.'
@@ -2689,7 +2698,7 @@ Metrics/AbcSize:
2689
2698
  A calculated magnitude based on number of assignments,
2690
2699
  branches, and conditions.
2691
2700
  References:
2692
- - http://c2.com/cgi/wiki?AbcMetric
2701
+ - https://wiki.c2.com/?AbcMetric
2693
2702
  - https://en.wikipedia.org/wiki/ABC_Software_Metric
2694
2703
  Enabled: true
2695
2704
  VersionAdded: '0.27'
@@ -3063,6 +3072,7 @@ Naming/PredicateMethod:
3063
3072
  Description: 'Checks that predicate methods end with `?` and non-predicate methods do not.'
3064
3073
  Enabled: pending
3065
3074
  VersionAdded: '1.76'
3075
+ VersionChanged: '1.76'
3066
3076
  # In `aggressive` mode, the cop will register an offense for predicate methods that
3067
3077
  # may return a non-boolean value.
3068
3078
  # In `conservative` mode, the cop will *not* register an offense for predicate methods
@@ -3070,6 +3080,8 @@ Naming/PredicateMethod:
3070
3080
  Mode: conservative
3071
3081
  AllowedMethods:
3072
3082
  - call
3083
+ AllowedPatterns: []
3084
+ AllowBangMethods: false
3073
3085
 
3074
3086
  Naming/PredicatePrefix:
3075
3087
  Description: 'Predicate method names should not be prefixed and end with a `?`.'
@@ -3657,6 +3669,13 @@ Style/CollectionMethods:
3657
3669
  - inject
3658
3670
  - reduce
3659
3671
 
3672
+ Style/CollectionQuerying:
3673
+ Description: 'Prefer `Enumerable` predicate methods over expressions with `count`.'
3674
+ StyleGuide: '#collection-querying'
3675
+ Enabled: pending
3676
+ VersionAdded: '1.77'
3677
+ Safe: false
3678
+
3660
3679
  Style/ColonMethodCall:
3661
3680
  Description: 'Do not use :: for method call.'
3662
3681
  StyleGuide: '#double-colons'
@@ -4058,6 +4077,9 @@ Style/FetchEnvVar:
4058
4077
  VersionAdded: '1.28'
4059
4078
  # Environment variables to be excluded from the inspection.
4060
4079
  AllowedVars: []
4080
+ # When `true`, autocorrects `ENV["key"]` to `ENV.fetch("key", nil)`.
4081
+ # When `false`, autocorrects `ENV["key"]` to `ENV.fetch("key")`.
4082
+ DefaultToNil: true
4061
4083
 
4062
4084
  Style/FileEmpty:
4063
4085
  Description: >-
@@ -5006,7 +5028,7 @@ Style/OpenStructUse:
5006
5028
  Avoid using OpenStruct. As of Ruby 3.0, use is officially discouraged due to performance,
5007
5029
  version compatibility, and potential security issues.
5008
5030
  References:
5009
- - https://docs.ruby-lang.org/en/3.0.0/OpenStruct.html#class-OpenStruct-label-Caveats
5031
+ - https://docs.ruby-lang.org/en/3.0/OpenStruct.html#class-OpenStruct-label-Caveats
5010
5032
 
5011
5033
  Enabled: pending
5012
5034
  Safe: false
@@ -10,8 +10,11 @@ module RuboCop
10
10
  COMMA_REGEXP = /(?<=\))\s*,/.freeze
11
11
 
12
12
  def correct(corrector, node)
13
- corrector.remove(node.loc.begin)
14
- corrector.remove(node.loc.end)
13
+ buffer = node.source_range.source_buffer
14
+ corrector.remove(range_with_surrounding_space(range: node.loc.begin, buffer: buffer,
15
+ side: :right, whitespace: true))
16
+ corrector.remove(range_with_surrounding_space(range: node.loc.end, buffer: buffer,
17
+ side: :left))
15
18
  handle_orphaned_comma(corrector, node)
16
19
 
17
20
  return unless ternary_condition?(node) && next_char_is_question_mark?(node)
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Gemspec
6
+ # Use consistent style for Gemspec attributes assignment.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # # This example uses two styles for assignment of metadata attribute.
12
+ # Gem::Specification.new do |spec|
13
+ # spec.metadata = { 'key' => 'value' }
14
+ # spec.metadata['another-key'] = 'another-value'
15
+ # end
16
+ #
17
+ # # good
18
+ # Gem::Specification.new do |spec|
19
+ # spec.metadata['key'] = 'value'
20
+ # spec.metadata['another-key'] = 'another-value'
21
+ # end
22
+ #
23
+ # # good
24
+ # Gem::Specification.new do |spec|
25
+ # spec.metadata = { 'key' => 'value', 'another-key' => 'another-value' }
26
+ # end
27
+ #
28
+ # # bad
29
+ # # This example uses two styles for assignment of authors attribute.
30
+ # Gem::Specification.new do |spec|
31
+ # spec.authors = %w[author-0 author-1]
32
+ # spec.authors[2] = 'author-2'
33
+ # end
34
+ #
35
+ # # good
36
+ # Gem::Specification.new do |spec|
37
+ # spec.authors = %w[author-0 author-1 author-2]
38
+ # end
39
+ #
40
+ # # good
41
+ # Gem::Specification.new do |spec|
42
+ # spec.authors[0] = 'author-0'
43
+ # spec.authors[1] = 'author-1'
44
+ # spec.authors[2] = 'author-2'
45
+ # end
46
+ #
47
+ # # good
48
+ # # This example uses consistent assignment per attribute,
49
+ # # even though two different styles are used overall.
50
+ # Gem::Specification.new do |spec|
51
+ # spec.metadata = { 'key' => 'value' }
52
+ # spec.authors[0] = 'author-0'
53
+ # spec.authors[1] = 'author-1'
54
+ # spec.authors[2] = 'author-2'
55
+ # end
56
+ #
57
+ class AttributeAssignment < Base
58
+ include GemspecHelp
59
+
60
+ MSG = 'Use consistent style for Gemspec attributes assignment.'
61
+
62
+ def on_new_investigation
63
+ return if processed_source.blank?
64
+
65
+ assignments = source_assignments(processed_source.ast)
66
+ indexed_assignments = source_indexed_assignments(processed_source.ast)
67
+
68
+ assignments.keys.intersection(indexed_assignments.keys).each do |attribute|
69
+ indexed_assignments[attribute].each do |node|
70
+ add_offense(node)
71
+ end
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ def source_assignments(ast)
78
+ assignment_method_declarations(ast)
79
+ .select(&:assignment_method?)
80
+ .group_by(&:method_name)
81
+ .transform_keys { |method_name| method_name.to_s.delete_suffix('=').to_sym }
82
+ end
83
+
84
+ def source_indexed_assignments(ast)
85
+ indexed_assignment_method_declarations(ast)
86
+ .group_by { |node| node.children.first.method_name }
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -54,22 +54,6 @@ module RuboCop
54
54
  MSG = '`%<assignment>s` method calls already given on line ' \
55
55
  '%<line_of_first_occurrence>d of the gemspec.'
56
56
 
57
- # @!method assignment_method_declarations(node)
58
- def_node_search :assignment_method_declarations, <<~PATTERN
59
- (send
60
- (lvar {#match_block_variable_name? :_1 :it}) _ ...)
61
- PATTERN
62
-
63
- # @!method indexed_assignment_method_declarations(node)
64
- def_node_search :indexed_assignment_method_declarations, <<~PATTERN
65
- (send
66
- (send (lvar {#match_block_variable_name? :_1 :it}) _)
67
- :[]=
68
- literal?
69
- _
70
- )
71
- PATTERN
72
-
73
57
  def on_new_investigation
74
58
  return if processed_source.blank?
75
59
 
@@ -96,12 +80,6 @@ module RuboCop
96
80
  end
97
81
  end
98
82
 
99
- def match_block_variable_name?(receiver_name)
100
- gem_specification(processed_source.ast) do |block_variable_name|
101
- return block_variable_name == receiver_name
102
- end
103
- end
104
-
105
83
  def duplicated_assignment_method_nodes
106
84
  assignment_method_declarations(processed_source.ast)
107
85
  .select(&:assignment_method?)
@@ -74,6 +74,14 @@ module RuboCop
74
74
  }
75
75
  PATTERN
76
76
 
77
+ # @!method metadata_assignment(node)
78
+ def_node_search :metadata_assignment, <<~PATTERN
79
+ `{
80
+ (send _ :metadata= _)
81
+ (send (send _ :metadata) :[]= (str _) _)
82
+ }
83
+ PATTERN
84
+
77
85
  # @!method rubygems_mfa_required(node)
78
86
  def_node_search :rubygems_mfa_required, <<~PATTERN
79
87
  (pair (str "rubygems_mfa_required") $_)
@@ -131,9 +139,15 @@ module RuboCop
131
139
  end
132
140
 
133
141
  def insert_mfa_required(corrector, node, block_var)
134
- corrector.insert_before(node.loc.end, <<~RUBY)
142
+ require_mfa_directive = <<~RUBY.strip
135
143
  #{block_var}.metadata['rubygems_mfa_required'] = 'true'
136
144
  RUBY
145
+
146
+ if (last_assignment = metadata_assignment(processed_source.ast).to_a.last)
147
+ corrector.insert_after(last_assignment, "\n#{require_mfa_directive}")
148
+ else
149
+ corrector.insert_before(node.loc.end, "#{require_mfa_directive}\n")
150
+ end
137
151
  end
138
152
 
139
153
  def change_value(corrector, value)
@@ -110,8 +110,8 @@ module RuboCop
110
110
  def directive_offense_type(directive, actual_name)
111
111
  return :missing_directive unless directive
112
112
 
113
- return :wrong_scope if wrong_scope(directive, actual_name)
114
- return :no_scope if no_scope(directive, actual_name)
113
+ return :wrong_scope if wrong_scope?(directive, actual_name)
114
+ return :no_scope if no_scope?(directive, actual_name)
115
115
 
116
116
  # The method directive being prefixed by 'self.' is always an offense.
117
117
  # The matched method_name does not contain the receiver but the
@@ -121,11 +121,11 @@ module RuboCop
121
121
  end
122
122
  end
123
123
 
124
- def wrong_scope(directive, actual_name)
124
+ def wrong_scope?(directive, actual_name)
125
125
  !actual_name.start_with?('self.') && directive[:has_scope_directive]
126
126
  end
127
127
 
128
- def no_scope(directive, actual_name)
128
+ def no_scope?(directive, actual_name)
129
129
  actual_name.start_with?('self.') && !directive[:has_scope_directive]
130
130
  end
131
131
 
@@ -161,7 +161,7 @@ module RuboCop
161
161
  elements.flat_map do |e|
162
162
  e.loc.column
163
163
  end
164
- end.uniq.count == 1
164
+ end.uniq.one?
165
165
  end
166
166
 
167
167
  def first_argument_line(elements)
@@ -138,7 +138,7 @@ module RuboCop
138
138
  end
139
139
 
140
140
  def previous_line_ignoring_comments(processed_source, send_line)
141
- processed_source[0..send_line - 2].reverse.find { |line| !comment_line?(line) }
141
+ processed_source[0..(send_line - 2)].reverse.find { |line| !comment_line?(line) }
142
142
  end
143
143
 
144
144
  def previous_line_empty?(send_line)
@@ -258,7 +258,7 @@ module RuboCop
258
258
  if ignore_cop_directives? && directive_on_source_line?(line_index)
259
259
  return check_directive_line(line, line_index)
260
260
  end
261
- return check_uri_line(line, line_index) if allow_uri?
261
+ return check_line_for_exemptions(line, line_index) if allow_uri? || allow_qualified_name?
262
262
 
263
263
  register_offense(excess_range(nil, line, line_index), line, line_index)
264
264
  end
@@ -358,11 +358,32 @@ module RuboCop
358
358
  )
359
359
  end
360
360
 
361
- def check_uri_line(line, line_index)
362
- uri_range = find_excessive_uri_range(line)
363
- return if uri_range && allowed_uri_position?(line, uri_range)
361
+ def check_line_for_exemptions(line, line_index)
362
+ uri_range = range_if_applicable(line, :uri)
363
+ qualified_name_range = range_if_applicable(line, :qualified_name)
364
364
 
365
- register_offense(excess_range(uri_range, line, line_index), line, line_index)
365
+ return if allowed_combination?(line, uri_range, qualified_name_range)
366
+
367
+ range = uri_range || qualified_name_range
368
+ register_offense(excess_range(range, line, line_index), line, line_index)
369
+ end
370
+
371
+ def range_if_applicable(line, type)
372
+ return unless type == :uri ? allow_uri? : allow_qualified_name?
373
+
374
+ find_excessive_range(line, type)
375
+ end
376
+
377
+ def allowed_combination?(line, uri_range, qualified_name_range)
378
+ if uri_range && qualified_name_range
379
+ allowed_position?(line, uri_range) && allowed_position?(line, qualified_name_range)
380
+ elsif uri_range
381
+ allowed_position?(line, uri_range)
382
+ elsif qualified_name_range
383
+ allowed_position?(line, qualified_name_range)
384
+ else
385
+ false
386
+ end
366
387
  end
367
388
 
368
389
  def breakable_dstr?(node)
@@ -22,10 +22,11 @@ module RuboCop
22
22
  RESTRICT_ON_SEND = %i[[] []=].freeze
23
23
 
24
24
  def on_send(node)
25
+ return if node.loc.dot
26
+
25
27
  receiver_end_pos = node.receiver.source_range.end_pos
26
28
  selector_begin_pos = node.loc.selector.begin_pos
27
29
  return if receiver_end_pos >= selector_begin_pos
28
- return if dot_before_brackets?(node, receiver_end_pos, selector_begin_pos)
29
30
 
30
31
  range = range_between(receiver_end_pos, selector_begin_pos)
31
32
 
@@ -33,14 +34,6 @@ module RuboCop
33
34
  corrector.remove(range)
34
35
  end
35
36
  end
36
-
37
- private
38
-
39
- def dot_before_brackets?(node, receiver_end_pos, selector_begin_pos)
40
- return false unless node.loc.respond_to?(:dot) && (dot = node.loc.dot)
41
-
42
- dot.begin_pos == receiver_end_pos && dot.end_pos == selector_begin_pos
43
- end
44
37
  end
45
38
  end
46
39
  end
@@ -86,6 +86,7 @@ module RuboCop
86
86
  def on_array(node)
87
87
  return if node.array_type? && !node.square_brackets?
88
88
 
89
+ node = find_node_with_brackets(node)
89
90
  tokens, left, right = array_brackets(node)
90
91
  return unless left && right
91
92
 
@@ -102,6 +103,10 @@ module RuboCop
102
103
 
103
104
  private
104
105
 
106
+ def find_node_with_brackets(node)
107
+ node.ancestors.find(&:const_pattern_type?) || node
108
+ end
109
+
105
110
  def autocorrect(corrector, node)
106
111
  tokens, left, right = array_brackets(node)
107
112
 
@@ -119,7 +124,7 @@ module RuboCop
119
124
  def array_brackets(node)
120
125
  tokens = processed_source.tokens_within(node)
121
126
 
122
- left = tokens.find(&:left_array_bracket?)
127
+ left = tokens.find(&:left_bracket?)
123
128
  right = tokens.reverse_each.find(&:right_bracket?)
124
129
 
125
130
  [tokens, left, right]
@@ -192,7 +197,7 @@ module RuboCop
192
197
  if side == :right
193
198
  processed_source.tokens_within(node)[i].right_bracket?
194
199
  else
195
- processed_source.tokens_within(node)[i].left_array_bracket?
200
+ processed_source.tokens_within(node)[i].left_bracket?
196
201
  end
197
202
  end
198
203
 
@@ -27,7 +27,9 @@ module RuboCop
27
27
  # @example
28
28
  # # bad
29
29
  # x || 1..2
30
+ # x - 1..2
30
31
  # (x || 1..2)
32
+ # x || 1..y || 2
31
33
  # 1..2.to_a
32
34
  #
33
35
  # # good, unambiguous
@@ -41,6 +43,7 @@ module RuboCop
41
43
  #
42
44
  # # good, ambiguity removed
43
45
  # x || (1..2)
46
+ # (x - 1)..2
44
47
  # (x || 1)..2
45
48
  # (x || 1)..(y || 2)
46
49
  # (1..2).to_a
@@ -96,6 +99,8 @@ module RuboCop
96
99
  # to avoid the ambiguity of `1..2.to_a`.
97
100
  return false if node.receiver&.basic_literal?
98
101
 
102
+ return false if node.operator_method? && !node.method?(:[])
103
+
99
104
  require_parentheses_for_method_chain? || node.receiver.nil?
100
105
  end
101
106
 
@@ -20,7 +20,7 @@ module RuboCop
20
20
 
21
21
  def on_interpolation(begin_node)
22
22
  node_children = begin_node.children.dup
23
- node_children.delete_if { |e| e&.nil_type? || (e&.basic_literal? && e&.value&.empty?) }
23
+ node_children.delete_if { |e| e.nil_type? || (e.basic_literal? && e.str_content&.empty?) }
24
24
  return unless node_children.empty?
25
25
 
26
26
  add_offense(begin_node) { |corrector| corrector.remove(begin_node) }
@@ -94,7 +94,7 @@ module RuboCop
94
94
  when :float
95
95
  true
96
96
  when :send
97
- check_send(node)
97
+ float_send?(node)
98
98
  when :begin
99
99
  float?(node.children.first)
100
100
  else
@@ -108,18 +108,18 @@ module RuboCop
108
108
  (node.numeric_type? && node.value.zero?) || node.nil_type?
109
109
  end
110
110
 
111
- def check_send(node)
111
+ def float_send?(node)
112
112
  if node.arithmetic_operation?
113
113
  float?(node.receiver) || float?(node.first_argument)
114
114
  elsif FLOAT_RETURNING_METHODS.include?(node.method_name)
115
115
  true
116
116
  elsif node.receiver&.float_type?
117
117
  FLOAT_INSTANCE_METHODS.include?(node.method_name) ||
118
- check_numeric_returning_method(node)
118
+ numeric_returning_method?(node)
119
119
  end
120
120
  end
121
121
 
122
- def check_numeric_returning_method(node)
122
+ def numeric_returning_method?(node)
123
123
  return false unless node.receiver
124
124
 
125
125
  case node.method_name
@@ -228,7 +228,7 @@ module RuboCop
228
228
  )
229
229
  end
230
230
 
231
- def condition_evaluation(node, cond)
231
+ def condition_evaluation?(node, cond)
232
232
  if node.unless?
233
233
  cond.falsey_literal?
234
234
  else
@@ -238,7 +238,7 @@ module RuboCop
238
238
 
239
239
  # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
240
240
  def correct_if_node(node, cond)
241
- result = condition_evaluation(node, cond)
241
+ result = condition_evaluation?(node, cond)
242
242
 
243
243
  new_node = if node.elsif? && result
244
244
  "else\n #{range_with_comments(node.if_branch).source}"
@@ -73,7 +73,7 @@ module RuboCop
73
73
  end
74
74
 
75
75
  def redundant_group?(expr)
76
- expr.is?(:passive, :group) && expr.count { |child| child.type != :free_space } == 1
76
+ expr.is?(:passive, :group) && expr.one? { |child| child.type != :free_space }
77
77
  end
78
78
 
79
79
  def redundantly_quantifiable?(node)
@@ -97,7 +97,7 @@ module RuboCop
97
97
  end
98
98
 
99
99
  def require_parentheses?(send_node)
100
- return true if operator_inside_hash?(send_node)
100
+ return true if operator_inside_collection_literal?(send_node)
101
101
  return false unless send_node.comparison_method?
102
102
  return false unless (node = send_node.parent)
103
103
 
@@ -105,10 +105,10 @@ module RuboCop
105
105
  (node.respond_to?(:comparison_method?) && node.comparison_method?)
106
106
  end
107
107
 
108
- def operator_inside_hash?(send_node)
109
- # If an operator call (without a dot) is inside a hash, it needs
108
+ def operator_inside_collection_literal?(send_node)
109
+ # If an operator call (without a dot) is inside an array or a hash, it needs
110
110
  # to be parenthesized when converted to safe navigation.
111
- send_node.parent&.pair_type? && !send_node.loc.dot
111
+ send_node.parent&.type?(:array, :pair) && !send_node.loc.dot
112
112
  end
113
113
  end
114
114
  end
@@ -23,6 +23,14 @@ module RuboCop
23
23
  # # good (method calls possibly can return different results)
24
24
  # hash[foo] = hash[foo]
25
25
  #
26
+ # @example AllowRBSInlineAnnotation:true
27
+ # # good
28
+ # foo = foo #: Integer
29
+ # foo, bar = foo, bar #: Integer
30
+ # Foo = Foo #: Integer
31
+ # hash['foo'] = hash['foo'] #: Integer
32
+ # obj.attr = obj.attr #: Integer
33
+ #
26
34
  class SelfAssignment < Base
27
35
  MSG = 'Self-assignment detected.'
28
36
 
@@ -34,6 +42,8 @@ module RuboCop
34
42
  }.freeze
35
43
 
36
44
  def on_send(node)
45
+ return if allow_rbs_inline_annotation? && rbs_inline_annotation?(node.receiver)
46
+
37
47
  if node.method?(:[]=)
38
48
  handle_key_assignment(node) if node.arguments.size == 2
39
49
  elsif node.assignment_method?
@@ -44,6 +54,7 @@ module RuboCop
44
54
 
45
55
  def on_lvasgn(node)
46
56
  return unless node.rhs
57
+ return if allow_rbs_inline_annotation? && rbs_inline_annotation?(node.rhs)
47
58
 
48
59
  rhs_type = ASSIGNMENT_TYPE_TO_RHS_TYPE[node.type]
49
60
 
@@ -55,16 +66,22 @@ module RuboCop
55
66
 
56
67
  def on_casgn(node)
57
68
  return unless node.rhs&.const_type?
69
+ return if allow_rbs_inline_annotation? && rbs_inline_annotation?(node.rhs)
58
70
 
59
71
  add_offense(node) if node.namespace == node.rhs.namespace &&
60
72
  node.short_name == node.rhs.short_name
61
73
  end
62
74
 
63
75
  def on_masgn(node)
76
+ first_lhs = node.lhs.assignments.first
77
+ return if allow_rbs_inline_annotation? && rbs_inline_annotation?(first_lhs)
78
+
64
79
  add_offense(node) if multiple_self_assignment?(node)
65
80
  end
66
81
 
67
82
  def on_or_asgn(node)
83
+ return if allow_rbs_inline_annotation? && rbs_inline_annotation?(node.lhs)
84
+
68
85
  add_offense(node) if rhs_matches_lhs?(node.rhs, node.lhs)
69
86
  end
70
87
  alias on_and_asgn on_or_asgn
@@ -108,6 +125,14 @@ module RuboCop
108
125
  add_offense(node)
109
126
  end
110
127
  end
128
+
129
+ def rbs_inline_annotation?(node)
130
+ processed_source.ast_with_comments[node].any? { |comment| comment.text.start_with?('#:') }
131
+ end
132
+
133
+ def allow_rbs_inline_annotation?
134
+ cop_config['AllowRBSInlineAnnotation']
135
+ end
111
136
  end
112
137
  end
113
138
  end