rubocop 1.36.0 → 1.39.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +31 -3
  4. data/lib/rubocop/arguments_env.rb +17 -0
  5. data/lib/rubocop/arguments_file.rb +17 -0
  6. data/lib/rubocop/cli/command/execute_runner.rb +7 -7
  7. data/lib/rubocop/cli/command/suggest_extensions.rb +8 -1
  8. data/lib/rubocop/comment_config.rb +36 -1
  9. data/lib/rubocop/cop/commissioner.rb +3 -1
  10. data/lib/rubocop/cop/generator.rb +1 -2
  11. data/lib/rubocop/cop/internal_affairs/create_empty_file.rb +37 -0
  12. data/lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb +111 -0
  13. data/lib/rubocop/cop/internal_affairs.rb +2 -0
  14. data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -0
  15. data/lib/rubocop/cop/layout/indentation_style.rb +1 -1
  16. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  17. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +29 -8
  18. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
  19. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +13 -9
  20. data/lib/rubocop/cop/layout/space_inside_array_percent_literal.rb +3 -0
  21. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +30 -3
  22. data/lib/rubocop/cop/layout/space_inside_percent_literal_delimiters.rb +34 -0
  23. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +6 -2
  24. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  25. data/lib/rubocop/cop/lint/duplicate_magic_comment.rb +73 -0
  26. data/lib/rubocop/cop/lint/duplicate_methods.rb +28 -9
  27. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +25 -6
  28. data/lib/rubocop/cop/lint/empty_class.rb +3 -1
  29. data/lib/rubocop/cop/lint/empty_conditional_body.rb +20 -8
  30. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +18 -3
  31. data/lib/rubocop/cop/lint/nested_method_definition.rb +50 -1
  32. data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
  33. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +4 -5
  34. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +1 -1
  35. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +23 -1
  36. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +7 -0
  37. data/lib/rubocop/cop/lint/redundant_require_statement.rb +38 -10
  38. data/lib/rubocop/cop/lint/require_parentheses.rb +1 -1
  39. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +3 -2
  40. data/lib/rubocop/cop/lint/shadowed_exception.rb +0 -10
  41. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +3 -0
  42. data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
  43. data/lib/rubocop/cop/lint/unreachable_loop.rb +1 -1
  44. data/lib/rubocop/cop/lint/unused_method_argument.rb +4 -0
  45. data/lib/rubocop/cop/metrics/abc_size.rb +1 -1
  46. data/lib/rubocop/cop/mixin/comments_help.rb +12 -0
  47. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +4 -0
  48. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +6 -3
  49. data/lib/rubocop/cop/mixin/range_help.rb +23 -0
  50. data/lib/rubocop/cop/mixin/rescue_node.rb +3 -1
  51. data/lib/rubocop/cop/mixin/surrounding_space.rb +10 -8
  52. data/lib/rubocop/cop/mixin/visibility_help.rb +40 -5
  53. data/lib/rubocop/cop/naming/inclusive_language.rb +1 -1
  54. data/lib/rubocop/cop/registry.rb +10 -4
  55. data/lib/rubocop/cop/style/access_modifier_declarations.rb +5 -7
  56. data/lib/rubocop/cop/style/accessor_grouping.rb +7 -3
  57. data/lib/rubocop/cop/style/block_delimiters.rb +2 -2
  58. data/lib/rubocop/cop/style/character_literal.rb +1 -1
  59. data/lib/rubocop/cop/style/class_equality_comparison.rb +8 -6
  60. data/lib/rubocop/cop/style/collection_compact.rb +12 -3
  61. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  62. data/lib/rubocop/cop/style/endless_method.rb +1 -1
  63. data/lib/rubocop/cop/style/explicit_block_argument.rb +4 -0
  64. data/lib/rubocop/cop/style/format_string_token.rb +1 -1
  65. data/lib/rubocop/cop/style/guard_clause.rb +62 -21
  66. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +1 -0
  67. data/lib/rubocop/cop/style/hash_each_methods.rb +32 -10
  68. data/lib/rubocop/cop/style/hash_except.rb +4 -0
  69. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  70. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +25 -2
  71. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +13 -2
  72. data/lib/rubocop/cop/style/module_function.rb +28 -6
  73. data/lib/rubocop/cop/style/negated_if_else_condition.rb +7 -1
  74. data/lib/rubocop/cop/style/numeric_predicate.rb +1 -1
  75. data/lib/rubocop/cop/style/object_then.rb +3 -0
  76. data/lib/rubocop/cop/style/operator_method_call.rb +53 -0
  77. data/lib/rubocop/cop/style/quoted_symbols.rb +1 -1
  78. data/lib/rubocop/cop/style/redundant_begin.rb +1 -0
  79. data/lib/rubocop/cop/style/redundant_condition.rb +5 -2
  80. data/lib/rubocop/cop/style/redundant_each.rb +116 -0
  81. data/lib/rubocop/cop/style/redundant_initialize.rb +3 -1
  82. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +8 -1
  83. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +12 -3
  84. data/lib/rubocop/cop/style/redundant_string_escape.rb +181 -0
  85. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -1
  86. data/lib/rubocop/cop/style/static_class.rb +32 -1
  87. data/lib/rubocop/cop/style/symbol_array.rb +2 -0
  88. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  89. data/lib/rubocop/cop/style/word_array.rb +2 -0
  90. data/lib/rubocop/cop/team.rb +3 -4
  91. data/lib/rubocop/cop/util.rb +1 -1
  92. data/lib/rubocop/cop/variable_force/variable_table.rb +1 -1
  93. data/lib/rubocop/cops_documentation_generator.rb +2 -1
  94. data/lib/rubocop/ext/processed_source.rb +2 -0
  95. data/lib/rubocop/formatter/disabled_config_formatter.rb +8 -2
  96. data/lib/rubocop/formatter/offense_count_formatter.rb +8 -5
  97. data/lib/rubocop/formatter/worst_offenders_formatter.rb +6 -3
  98. data/lib/rubocop/options.rb +19 -15
  99. data/lib/rubocop/rspec/cop_helper.rb +21 -1
  100. data/lib/rubocop/rspec/shared_contexts.rb +14 -1
  101. data/lib/rubocop/runner.rb +15 -11
  102. data/lib/rubocop/server/cache.rb +5 -1
  103. data/lib/rubocop/server/cli.rb +9 -2
  104. data/lib/rubocop/server/client_command/exec.rb +5 -0
  105. data/lib/rubocop/server/core.rb +18 -1
  106. data/lib/rubocop/server/socket_reader.rb +5 -1
  107. data/lib/rubocop/server.rb +1 -1
  108. data/lib/rubocop/version.rb +8 -3
  109. data/lib/rubocop.rb +4 -0
  110. metadata +13 -5
@@ -50,6 +50,10 @@ module RuboCop
50
50
  (block $_ (args $...) (yield $...))
51
51
  PATTERN
52
52
 
53
+ def self.autocorrect_incompatible_with
54
+ [Lint::UnusedMethodArgument]
55
+ end
56
+
53
57
  def initialize(config = nil, options = nil)
54
58
  super
55
59
  @def_nodes = Set.new
@@ -77,7 +77,7 @@ module RuboCop
77
77
  # # bad
78
78
  # redirect('foo/%{bar_id}')
79
79
  #
80
- # @example AllowedPatterns: [/redirect/]
80
+ # @example AllowedPatterns: ['redirect']
81
81
  #
82
82
  # # good
83
83
  # redirect('foo/%{bar_id}')
@@ -10,6 +10,9 @@ module RuboCop
10
10
  # one of `return`, `break`, `next`, `raise`, or `fail` is used
11
11
  # in the body of the conditional expression.
12
12
  #
13
+ # NOTE: Autocorrect works in most cases except with if-else statements
14
+ # that contain logical operators such as `foo || raise('exception')`
15
+ #
13
16
  # @example
14
17
  # # bad
15
18
  # def test
@@ -90,6 +93,7 @@ module RuboCop
90
93
  # end
91
94
  #
92
95
  class GuardClause < Base
96
+ extend AutoCorrector
93
97
  include MinBodyLength
94
98
  include StatementModifier
95
99
 
@@ -101,40 +105,49 @@ module RuboCop
101
105
 
102
106
  return unless body
103
107
 
104
- if body.if_type?
105
- check_ending_if(body)
106
- elsif body.begin_type?
107
- final_expression = body.children.last
108
- check_ending_if(final_expression) if final_expression&.if_type?
109
- end
108
+ check_ending_body(body)
110
109
  end
111
110
  alias on_defs on_def
112
111
 
113
112
  def on_if(node)
114
113
  return if accepted_form?(node)
115
114
 
116
- guard_clause_in_if = node.if_branch&.guard_clause?
117
- guard_clause_in_else = node.else_branch&.guard_clause?
118
- guard_clause = guard_clause_in_if || guard_clause_in_else
119
- return unless guard_clause
115
+ if (guard_clause = node.if_branch&.guard_clause?)
116
+ kw = node.loc.keyword.source
117
+ guard = :if
118
+ elsif (guard_clause = node.else_branch&.guard_clause?)
119
+ kw = node.inverse_keyword
120
+ guard = :else
121
+ else
122
+ return
123
+ end
120
124
 
121
- kw = if guard_clause_in_if
122
- node.loc.keyword.source
123
- else
124
- node.inverse_keyword
125
- end
125
+ guard = nil if and_or_guard_clause?(guard_clause)
126
126
 
127
- register_offense(node, guard_clause_source(guard_clause), kw)
127
+ register_offense(node, guard_clause_source(guard_clause), kw, guard)
128
128
  end
129
129
 
130
130
  private
131
131
 
132
+ def check_ending_body(body)
133
+ return if body.nil?
134
+
135
+ if body.if_type?
136
+ check_ending_if(body)
137
+ elsif body.begin_type?
138
+ final_expression = body.children.last
139
+ check_ending_if(final_expression) if final_expression&.if_type?
140
+ end
141
+ end
142
+
132
143
  def check_ending_if(node)
133
144
  return if accepted_form?(node, ending: true) || !min_body_length?(node)
134
145
  return if allowed_consecutive_conditionals? &&
135
146
  consecutive_conditionals?(node.parent, node)
136
147
 
137
148
  register_offense(node, 'return', node.inverse_keyword)
149
+
150
+ check_ending_body(node.if_branch)
138
151
  end
139
152
 
140
153
  def consecutive_conditionals?(parent, node)
@@ -145,28 +158,56 @@ module RuboCop
145
158
  end
146
159
  end
147
160
 
148
- def register_offense(node, scope_exiting_keyword, conditional_keyword)
161
+ def register_offense(node, scope_exiting_keyword, conditional_keyword, guard = nil)
149
162
  condition, = node.node_parts
150
163
  example = [scope_exiting_keyword, conditional_keyword, condition.source].join(' ')
151
164
  if too_long_for_single_line?(node, example)
152
165
  return if trivial?(node)
153
166
 
154
167
  example = "#{conditional_keyword} #{condition.source}; #{scope_exiting_keyword}; end"
168
+ replacement = <<~RUBY.chomp
169
+ #{conditional_keyword} #{condition.source}
170
+ #{scope_exiting_keyword}
171
+ end
172
+ RUBY
155
173
  end
156
174
 
157
- add_offense(node.loc.keyword, message: format(MSG, example: example))
175
+ add_offense(node.loc.keyword, message: format(MSG, example: example)) do |corrector|
176
+ next if node.else? && guard.nil?
177
+
178
+ autocorrect(corrector, node, condition, replacement || example, guard)
179
+ end
158
180
  end
159
181
 
160
- def guard_clause_source(guard_clause)
161
- parent = guard_clause.parent
182
+ def autocorrect(corrector, node, condition, replacement, guard)
183
+ corrector.replace(node.loc.keyword.join(condition.loc.expression), replacement)
184
+ corrector.remove(node.loc.end)
185
+ return unless node.else?
186
+
187
+ corrector.remove(node.loc.else)
188
+ corrector.remove(branch_to_remove(node, guard))
189
+ end
190
+
191
+ def branch_to_remove(node, guard)
192
+ case guard
193
+ when :if then node.if_branch
194
+ when :else then node.else_branch
195
+ end
196
+ end
162
197
 
163
- if parent.and_type? || parent.or_type?
198
+ def guard_clause_source(guard_clause)
199
+ if and_or_guard_clause?(guard_clause)
164
200
  guard_clause.parent.source
165
201
  else
166
202
  guard_clause.source
167
203
  end
168
204
  end
169
205
 
206
+ def and_or_guard_clause?(guard_clause)
207
+ parent = guard_clause.parent
208
+ parent.and_type? || parent.or_type?
209
+ end
210
+
170
211
  def too_long_for_single_line?(node, example)
171
212
  max = max_line_length
172
213
  max && node.source_range.column + example.length > max
@@ -34,6 +34,7 @@ module RuboCop
34
34
  extend AutoCorrector
35
35
 
36
36
  def on_hash(node)
37
+ return if node.children.first&.kwsplat_type?
37
38
  return unless (array = containing_array(node))
38
39
  return unless last_array_item?(array, node) && explicit_array?(array)
39
40
 
@@ -38,28 +38,50 @@ module RuboCop
38
38
  ({block numblock} $(send (send _ ${:keys :values}) :each) ...)
39
39
  PATTERN
40
40
 
41
+ # @!method kv_each_with_block_pass(node)
42
+ def_node_matcher :kv_each_with_block_pass, <<~PATTERN
43
+ (send $(send _ ${:keys :values}) :each (block_pass (sym _)))
44
+ PATTERN
45
+
41
46
  def on_block(node)
42
- register_kv_offense(node)
47
+ kv_each(node) do |target, method|
48
+ register_kv_offense(target, method)
49
+ end
43
50
  end
44
51
 
45
52
  alias on_numblock on_block
46
53
 
54
+ def on_block_pass(node)
55
+ kv_each_with_block_pass(node.parent) do |target, method|
56
+ register_kv_with_block_pass_offense(node, target, method)
57
+ end
58
+ end
59
+
47
60
  private
48
61
 
49
- def register_kv_offense(node)
50
- kv_each(node) do |target, method|
51
- parent_receiver = target.receiver.receiver
52
- return unless parent_receiver
53
- return if allowed_receiver?(parent_receiver)
62
+ def register_kv_offense(target, method)
63
+ return unless (parent_receiver = target.receiver.receiver)
64
+ return if allowed_receiver?(parent_receiver)
65
+
66
+ add_offense(kv_range(target), message: format_message(method)) do |corrector|
67
+ correct_key_value_each(target, corrector)
68
+ end
69
+ end
54
70
 
55
- msg = format(message, prefer: "each_#{method[0..-2]}", current: "#{method}.each")
71
+ def register_kv_with_block_pass_offense(node, target, method)
72
+ return unless (parent_receiver = node.parent.receiver.receiver)
73
+ return if allowed_receiver?(parent_receiver)
56
74
 
57
- add_offense(kv_range(target), message: msg) do |corrector|
58
- correct_key_value_each(target, corrector)
59
- end
75
+ range = target.loc.selector.with(end_pos: node.parent.loc.selector.end_pos)
76
+ add_offense(range, message: format_message(method)) do |corrector|
77
+ corrector.replace(range, "each_#{method[0..-2]}")
60
78
  end
61
79
  end
62
80
 
81
+ def format_message(method_name)
82
+ format(MSG, prefer: "each_#{method_name[0..-2]}", current: "#{method_name}.each")
83
+ end
84
+
63
85
  def check_argument(variable)
64
86
  return unless variable.block_argument?
65
87
 
@@ -13,6 +13,10 @@ module RuboCop
13
13
  # when used `==`.
14
14
  # And do not check `Hash#delete_if` and `Hash#keep_if` to change receiver object.
15
15
  #
16
+ # @safety
17
+ # This cop is unsafe because it cannot be guaranteed that the receiver
18
+ # is a `Hash` or responds to the replacement method.
19
+ #
16
20
  # @example
17
21
  #
18
22
  # # bad
@@ -28,7 +28,7 @@ module RuboCop
28
28
  # * always - forces use of the 3.1 syntax (e.g. {foo:})
29
29
  # * never - forces use of explicit hash literal value
30
30
  # * either - accepts both shorthand and explicit use of hash literal value
31
- # * consistent - like "always", but will avoid mixing styles in a single hash
31
+ # * consistent - like "either", but will avoid mixing styles in a single hash
32
32
  #
33
33
  # @example EnforcedStyle: ruby19 (default)
34
34
  # # bad
@@ -5,10 +5,27 @@ module RuboCop
5
5
  module Style
6
6
  # Checks for redundant `if` with boolean literal branches.
7
7
  # It checks only conditions to return boolean value (`true` or `false`) for safe detection.
8
- # The conditions to be checked are comparison methods, predicate methods, and double negative.
8
+ # The conditions to be checked are comparison methods, predicate methods, and
9
+ # double negation (!!).
9
10
  # `nonzero?` method is allowed by default.
10
11
  # These are customizable with `AllowedMethods` option.
11
12
  #
13
+ # This cop targets only `if`s with a single `elsif` or `else` branch. The following
14
+ # code will be allowed, because it has two `elsif` branches:
15
+ #
16
+ # [source,ruby]
17
+ # ----
18
+ # if foo
19
+ # true
20
+ # elsif bar > baz
21
+ # true
22
+ # elsif qux > quux # Single `elsif` is warned, but two or more `elsif`s are not.
23
+ # true
24
+ # else
25
+ # false
26
+ # end
27
+ # ----
28
+ #
12
29
  # @safety
13
30
  # Autocorrection is unsafe because there is no guarantee that all predicate methods
14
31
  # will return a boolean value. Those methods can be allowed with `AllowedMethods` config.
@@ -57,7 +74,7 @@ module RuboCop
57
74
  def_node_matcher :double_negative?, '(send (send _ :!) :!)'
58
75
 
59
76
  def on_if(node)
60
- return unless if_with_boolean_literal_branches?(node)
77
+ return if !if_with_boolean_literal_branches?(node) || multiple_elsif?(node)
61
78
 
62
79
  condition = node.condition
63
80
  range, keyword = offense_range_with_keyword(node, condition)
@@ -76,6 +93,12 @@ module RuboCop
76
93
 
77
94
  private
78
95
 
96
+ def multiple_elsif?(node)
97
+ return false unless (parent = node.parent)
98
+
99
+ parent.if_type? && parent.elsif?
100
+ end
101
+
79
102
  def offense_range_with_keyword(node, condition)
80
103
  if node.ternary?
81
104
  range = condition.source_range.end.join(node.source_range.end)
@@ -20,8 +20,8 @@ module RuboCop
20
20
  return if require_parentheses_for_hash_value_omission?(node)
21
21
  return if syntax_like_method_call?(node)
22
22
  return if super_call_without_arguments?(node)
23
- return if allowed_camel_case_method_call?(node)
24
23
  return if legitimate_call_with_parentheses?(node)
24
+ return if allowed_camel_case_method_call?(node)
25
25
  return if allowed_string_interpolation_method_call?(node)
26
26
 
27
27
  add_offense(offense_range(node), message: OMIT_MSG) do |corrector|
@@ -97,7 +97,8 @@ module RuboCop
97
97
  call_in_optional_arguments?(node) ||
98
98
  call_in_single_line_inheritance?(node) ||
99
99
  allowed_multiline_call_with_parentheses?(node) ||
100
- allowed_chained_call_with_parentheses?(node)
100
+ allowed_chained_call_with_parentheses?(node) ||
101
+ assignment_in_condition?(node)
101
102
  end
102
103
 
103
104
  def call_in_literals?(node)
@@ -202,6 +203,16 @@ module RuboCop
202
203
  def inside_string_interpolation?(node)
203
204
  node.ancestors.drop_while { |a| !a.begin_type? }.any?(&:dstr_type?)
204
205
  end
206
+
207
+ def assignment_in_condition?(node)
208
+ parent = node.parent
209
+ return false unless parent
210
+
211
+ grandparent = parent.parent
212
+ return false unless grandparent
213
+
214
+ parent.assignment? && (grandparent.conditional? || grandparent.when_type?)
215
+ end
205
216
  end
206
217
  # rubocop:enable Metrics/ModuleLength, Metrics/CyclomaticComplexity
207
218
  end
@@ -3,13 +3,15 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks for use of `extend self` or `module_function` in a
7
- # module.
6
+ # Checks for use of `extend self` or `module_function` in a module.
8
7
  #
9
- # Supported styles are: module_function, extend_self, forbidden. `forbidden`
10
- # style prohibits the usage of both styles.
8
+ # Supported styles are: `module_function` (default), `extend_self` and `forbidden`.
11
9
  #
12
- # NOTE: the cop won't be activated when the module contains any private methods.
10
+ # A couple of things to keep in mind:
11
+ #
12
+ # - `forbidden` style prohibits the usage of both styles
13
+ # - in default mode (`module_function`), the cop won't be activated when the module
14
+ # contains any private methods
13
15
  #
14
16
  # @safety
15
17
  # Autocorrection is unsafe (and is disabled by default) because `extend self`
@@ -28,7 +30,6 @@ module RuboCop
28
30
  # # ...
29
31
  # end
30
32
  #
31
- # @example EnforcedStyle: module_function (default)
32
33
  # # good
33
34
  # module Test
34
35
  # extend self
@@ -37,6 +38,13 @@ module RuboCop
37
38
  # # ...
38
39
  # end
39
40
  #
41
+ # # good
42
+ # module Test
43
+ # class << self
44
+ # # ...
45
+ # end
46
+ # end
47
+ #
40
48
  # @example EnforcedStyle: extend_self
41
49
  # # bad
42
50
  # module Test
@@ -50,6 +58,13 @@ module RuboCop
50
58
  # # ...
51
59
  # end
52
60
  #
61
+ # # good
62
+ # module Test
63
+ # class << self
64
+ # # ...
65
+ # end
66
+ # end
67
+ #
53
68
  # @example EnforcedStyle: forbidden
54
69
  # # bad
55
70
  # module Test
@@ -70,6 +85,13 @@ module RuboCop
70
85
  # private
71
86
  # # ...
72
87
  # end
88
+ #
89
+ # # good
90
+ # module Test
91
+ # class << self
92
+ # # ...
93
+ # end
94
+ # end
73
95
  class ModuleFunction < Base
74
96
  include ConfigurableEnforcedStyle
75
97
  extend AutoCorrector
@@ -49,7 +49,8 @@ module RuboCop
49
49
  def on_if(node)
50
50
  return unless if_else?(node)
51
51
 
52
- condition = node.condition
52
+ condition = unwrap_begin_nodes(node.condition)
53
+
53
54
  return if double_negation?(condition) || !negated_condition?(condition)
54
55
 
55
56
  type = node.ternary? ? 'ternary' : 'if-else'
@@ -71,6 +72,11 @@ module RuboCop
71
72
  !node.elsif? && else_branch && (!else_branch.if_type? || !else_branch.elsif?)
72
73
  end
73
74
 
75
+ def unwrap_begin_nodes(node)
76
+ node = node.children.first while node.begin_type? || node.kwbegin_type?
77
+ node
78
+ end
79
+
74
80
  def negated_condition?(node)
75
81
  node.send_type? &&
76
82
  (node.negation_method? || NEGATED_EQUALITY_METHODS.include?(node.method_name))
@@ -66,7 +66,7 @@ module RuboCop
66
66
  # foo.negative?
67
67
  # bar.baz.positive?
68
68
  #
69
- # @example AllowedPatterns: [/zero/] with EnforcedStyle: predicate
69
+ # @example AllowedPatterns: ['zero'] with EnforcedStyle: predicate
70
70
  # # good
71
71
  # # bad
72
72
  # foo.zero?
@@ -25,6 +25,9 @@ module RuboCop
25
25
  class ObjectThen < Base
26
26
  include ConfigurableEnforcedStyle
27
27
  extend AutoCorrector
28
+ extend TargetRubyVersion
29
+
30
+ minimum_target_ruby_version 2.6
28
31
 
29
32
  MSG = 'Prefer `%<prefer>s` over `%<current>s`.'
30
33
 
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for redundant dot before operator method call.
7
+ # The target operator methods are `|`, `^`, `&`, `<=>`, `==`, `===`, `=~`, `>`, `>=`, `<`,
8
+ # `<=`, `<<`, `>>`, `+`, `-`, `*`, `/`, `%`, `**`, `~`, `!`, `!=`, and `!~`.
9
+ #
10
+ # @example
11
+ #
12
+ # # bad
13
+ # foo.+ bar
14
+ # foo.& bar
15
+ #
16
+ # # good
17
+ # foo + bar
18
+ # foo & bar
19
+ #
20
+ class OperatorMethodCall < Base
21
+ extend AutoCorrector
22
+
23
+ MSG = 'Redundant dot detected.'
24
+ RESTRICT_ON_SEND = %i[| ^ & <=> == === =~ > >= < <= << >> + - * / % ** ~ ! != !~].freeze
25
+
26
+ def on_send(node)
27
+ return unless (dot = node.loc.dot)
28
+ return if node.receiver.const_type?
29
+
30
+ _lhs, _op, rhs = *node
31
+ return if rhs.nil? || rhs.children.first
32
+
33
+ add_offense(dot) do |corrector|
34
+ wrap_in_parentheses_if_chained(corrector, node)
35
+ corrector.replace(dot, ' ')
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def wrap_in_parentheses_if_chained(corrector, node)
42
+ return unless node.parent&.call_type?
43
+
44
+ operator = node.loc.selector
45
+
46
+ ParenthesesCorrector.correct(corrector, node)
47
+ corrector.insert_after(operator, ' ')
48
+ corrector.wrap(node, '(', ')')
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -93,7 +93,7 @@ module RuboCop
93
93
  end
94
94
 
95
95
  # The conversion process doubles escaped slashes, so they have to be reverted
96
- correction.gsub('\\\\', '\\')
96
+ correction.gsub('\\\\', '\\').gsub('\"', '"')
97
97
  end
98
98
 
99
99
  def style
@@ -75,6 +75,7 @@ module RuboCop
75
75
 
76
76
  def on_def(node)
77
77
  return unless node.body&.kwbegin_type?
78
+ return if node.endless? && !node.body.children.one?
78
79
 
79
80
  register_offense(node.body)
80
81
  end
@@ -145,11 +145,14 @@ module RuboCop
145
145
 
146
146
  return false unless if_branch && else_branch
147
147
 
148
- if_branch.send_type? && if_branch.arguments.count == 1 &&
149
- else_branch.send_type? && else_branch.arguments.count == 1 &&
148
+ single_argument_method?(if_branch) && single_argument_method?(else_branch) &&
150
149
  same_method?(if_branch, else_branch)
151
150
  end
152
151
 
152
+ def single_argument_method?(node)
153
+ node.send_type? && !node.method?(:[]) && node.arguments.one?
154
+ end
155
+
153
156
  def same_method?(if_branch, else_branch)
154
157
  if_branch.method?(else_branch.method_name) && if_branch.receiver == else_branch.receiver
155
158
  end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for redundant `each`.
7
+ #
8
+ # @safety
9
+ # This cop is unsafe, as it can produce false positives if the receiver
10
+ # is not an `Enumerator`.
11
+ #
12
+ # @example
13
+ #
14
+ # # bad
15
+ # array.each.each { |v| do_something(v) }
16
+ #
17
+ # # good
18
+ # array.each { |v| do_something(v) }
19
+ #
20
+ # # bad
21
+ # array.each.each_with_index { |v, i| do_something(v, i) }
22
+ #
23
+ # # good
24
+ # array.each.with_index { |v, i| do_something(v, i) }
25
+ # array.each_with_index { |v, i| do_something(v, i) }
26
+ #
27
+ # # bad
28
+ # array.each.each_with_object { |v, o| do_something(v, o) }
29
+ #
30
+ # # good
31
+ # array.each.with_object { |v, o| do_something(v, o) }
32
+ # array.each_with_object { |v, o| do_something(v, o) }
33
+ #
34
+ class RedundantEach < Base
35
+ extend AutoCorrector
36
+
37
+ MSG = 'Remove redundant `each`.'
38
+ MSG_WITH_INDEX = 'Use `with_index` to remove redundant `each`.'
39
+ MSG_WITH_OBJECT = 'Use `with_object` to remove redundant `each`.'
40
+
41
+ RESTRICT_ON_SEND = %i[each each_with_index each_with_object].freeze
42
+
43
+ def on_send(node)
44
+ return unless (redundant_node = redundant_each_method(node))
45
+
46
+ range = range(node)
47
+
48
+ add_offense(range, message: message(node)) do |corrector|
49
+ case node.method_name
50
+ when :each
51
+ remove_redundant_each(corrector, range, redundant_node)
52
+ when :each_with_index
53
+ corrector.replace(node.loc.selector, 'with_index')
54
+ when :each_with_object
55
+ corrector.replace(node.loc.selector, 'with_object')
56
+ end
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
63
+ def redundant_each_method(node)
64
+ return if node.last_argument&.block_pass_type?
65
+
66
+ if node.method?(:each) && !node.parent&.block_type?
67
+ ancestor_node = node.each_ancestor(:send).detect do |ancestor|
68
+ ancestor.receiver == node &&
69
+ (RESTRICT_ON_SEND.include?(ancestor.method_name) || ancestor.method?(:reverse_each))
70
+ end
71
+
72
+ return ancestor_node if ancestor_node
73
+ end
74
+
75
+ return unless (prev_method = node.children.first)
76
+ return if !prev_method.send_type? ||
77
+ prev_method.parent.block_type? || prev_method.last_argument&.block_pass_type?
78
+
79
+ detected = prev_method.method_name.to_s.start_with?('each_') unless node.method?(:each)
80
+
81
+ prev_method if detected || prev_method.method?(:reverse_each)
82
+ end
83
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
84
+
85
+ def range(node)
86
+ if node.method?(:each)
87
+ node.loc.dot.join(node.loc.selector)
88
+ else
89
+ node.loc.selector
90
+ end
91
+ end
92
+
93
+ def message(node)
94
+ case node.method_name
95
+ when :each
96
+ MSG
97
+ when :each_with_index
98
+ MSG_WITH_INDEX
99
+ when :each_with_object
100
+ MSG_WITH_OBJECT
101
+ end
102
+ end
103
+
104
+ def remove_redundant_each(corrector, range, redundant_node)
105
+ corrector.remove(range)
106
+
107
+ if redundant_node.method?(:each_with_index)
108
+ corrector.replace(redundant_node.loc.selector, 'each.with_index')
109
+ elsif redundant_node.method?(:each_with_object)
110
+ corrector.replace(redundant_node.loc.selector, 'each.with_object')
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -140,7 +140,9 @@ module RuboCop
140
140
  end
141
141
 
142
142
  def allow_comments?(node)
143
- cop_config['AllowComments'] && contains_comments?(node)
143
+ return false unless cop_config['AllowComments']
144
+
145
+ contains_comments?(node) && !comments_contain_disables?(node, name)
144
146
  end
145
147
 
146
148
  def same_args?(super_node, args)