rubocop 1.36.0 → 1.40.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 (150) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +78 -12
  4. data/exe/rubocop +1 -1
  5. data/lib/rubocop/arguments_env.rb +17 -0
  6. data/lib/rubocop/arguments_file.rb +17 -0
  7. data/lib/rubocop/cli/command/execute_runner.rb +7 -7
  8. data/lib/rubocop/cli/command/suggest_extensions.rb +8 -1
  9. data/lib/rubocop/comment_config.rb +41 -1
  10. data/lib/rubocop/config.rb +5 -4
  11. data/lib/rubocop/config_loader.rb +5 -5
  12. data/lib/rubocop/config_loader_resolver.rb +1 -1
  13. data/lib/rubocop/cop/base.rb +2 -9
  14. data/lib/rubocop/cop/commissioner.rb +3 -1
  15. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +22 -6
  16. data/lib/rubocop/cop/generator.rb +1 -2
  17. data/lib/rubocop/cop/internal_affairs/create_empty_file.rb +37 -0
  18. data/lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb +111 -0
  19. data/lib/rubocop/cop/internal_affairs/lambda_or_proc.rb +46 -0
  20. data/lib/rubocop/cop/internal_affairs.rb +3 -0
  21. data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -0
  22. data/lib/rubocop/cop/layout/indentation_style.rb +1 -1
  23. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  24. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +29 -8
  25. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
  26. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +13 -9
  27. data/lib/rubocop/cop/layout/space_inside_array_percent_literal.rb +3 -0
  28. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +30 -3
  29. data/lib/rubocop/cop/layout/space_inside_percent_literal_delimiters.rb +34 -0
  30. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +6 -2
  31. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +2 -2
  32. data/lib/rubocop/cop/lint/assignment_in_condition.rb +11 -1
  33. data/lib/rubocop/cop/lint/deprecated_constants.rb +8 -1
  34. data/lib/rubocop/cop/lint/duplicate_magic_comment.rb +73 -0
  35. data/lib/rubocop/cop/lint/duplicate_methods.rb +28 -9
  36. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +25 -6
  37. data/lib/rubocop/cop/lint/empty_block.rb +1 -5
  38. data/lib/rubocop/cop/lint/empty_class.rb +3 -1
  39. data/lib/rubocop/cop/lint/empty_conditional_body.rb +21 -9
  40. data/lib/rubocop/cop/lint/interpolation_check.rb +4 -3
  41. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +18 -3
  42. data/lib/rubocop/cop/lint/nested_method_definition.rb +50 -1
  43. data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
  44. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +4 -5
  45. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +1 -1
  46. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +5 -0
  47. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +36 -4
  48. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +7 -0
  49. data/lib/rubocop/cop/lint/redundant_require_statement.rb +38 -10
  50. data/lib/rubocop/cop/lint/require_parentheses.rb +1 -1
  51. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +18 -8
  52. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +5 -4
  53. data/lib/rubocop/cop/lint/shadowed_exception.rb +0 -10
  54. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +7 -3
  55. data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
  56. data/lib/rubocop/cop/lint/unreachable_loop.rb +1 -1
  57. data/lib/rubocop/cop/lint/unused_method_argument.rb +4 -0
  58. data/lib/rubocop/cop/lint/void.rb +6 -6
  59. data/lib/rubocop/cop/metrics/abc_size.rb +1 -1
  60. data/lib/rubocop/cop/metrics/block_length.rb +9 -4
  61. data/lib/rubocop/cop/metrics/class_length.rb +9 -4
  62. data/lib/rubocop/cop/metrics/method_length.rb +9 -4
  63. data/lib/rubocop/cop/metrics/module_length.rb +9 -4
  64. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +5 -2
  65. data/lib/rubocop/cop/mixin/comments_help.rb +12 -0
  66. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +4 -0
  67. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +30 -8
  68. data/lib/rubocop/cop/mixin/range_help.rb +23 -0
  69. data/lib/rubocop/cop/mixin/rescue_node.rb +3 -1
  70. data/lib/rubocop/cop/mixin/statement_modifier.rb +15 -1
  71. data/lib/rubocop/cop/mixin/surrounding_space.rb +10 -8
  72. data/lib/rubocop/cop/mixin/visibility_help.rb +40 -5
  73. data/lib/rubocop/cop/naming/inclusive_language.rb +1 -1
  74. data/lib/rubocop/cop/registry.rb +32 -14
  75. data/lib/rubocop/cop/style/access_modifier_declarations.rb +5 -7
  76. data/lib/rubocop/cop/style/accessor_grouping.rb +7 -3
  77. data/lib/rubocop/cop/style/array_intersect.rb +111 -0
  78. data/lib/rubocop/cop/style/block_delimiters.rb +2 -2
  79. data/lib/rubocop/cop/style/character_literal.rb +1 -1
  80. data/lib/rubocop/cop/style/class_equality_comparison.rb +8 -6
  81. data/lib/rubocop/cop/style/collection_compact.rb +12 -3
  82. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  83. data/lib/rubocop/cop/style/endless_method.rb +1 -1
  84. data/lib/rubocop/cop/style/explicit_block_argument.rb +4 -0
  85. data/lib/rubocop/cop/style/format_string_token.rb +1 -1
  86. data/lib/rubocop/cop/style/guard_clause.rb +90 -22
  87. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +1 -0
  88. data/lib/rubocop/cop/style/hash_each_methods.rb +32 -10
  89. data/lib/rubocop/cop/style/hash_except.rb +4 -0
  90. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  91. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +25 -2
  92. data/lib/rubocop/cop/style/if_with_semicolon.rb +2 -1
  93. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +13 -2
  94. data/lib/rubocop/cop/style/module_function.rb +28 -6
  95. data/lib/rubocop/cop/style/negated_if_else_condition.rb +7 -1
  96. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  97. data/lib/rubocop/cop/style/numeric_predicate.rb +1 -1
  98. data/lib/rubocop/cop/style/object_then.rb +3 -0
  99. data/lib/rubocop/cop/style/operator_method_call.rb +53 -0
  100. data/lib/rubocop/cop/style/quoted_symbols.rb +1 -1
  101. data/lib/rubocop/cop/style/redundant_argument.rb +3 -0
  102. data/lib/rubocop/cop/style/redundant_begin.rb +1 -0
  103. data/lib/rubocop/cop/style/redundant_condition.rb +5 -2
  104. data/lib/rubocop/cop/style/redundant_constant_base.rb +72 -0
  105. data/lib/rubocop/cop/style/redundant_each.rb +116 -0
  106. data/lib/rubocop/cop/style/redundant_initialize.rb +3 -1
  107. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +8 -1
  108. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +12 -3
  109. data/lib/rubocop/cop/style/redundant_return.rb +7 -0
  110. data/lib/rubocop/cop/style/redundant_sort.rb +1 -1
  111. data/lib/rubocop/cop/style/redundant_string_escape.rb +181 -0
  112. data/lib/rubocop/cop/style/require_order.rb +88 -0
  113. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -1
  114. data/lib/rubocop/cop/style/safe_navigation.rb +35 -6
  115. data/lib/rubocop/cop/style/select_by_regexp.rb +8 -4
  116. data/lib/rubocop/cop/style/static_class.rb +32 -1
  117. data/lib/rubocop/cop/style/string_literals.rb +1 -5
  118. data/lib/rubocop/cop/style/symbol_array.rb +2 -0
  119. data/lib/rubocop/cop/style/symbol_proc.rb +3 -5
  120. data/lib/rubocop/cop/style/word_array.rb +2 -0
  121. data/lib/rubocop/cop/team.rb +4 -5
  122. data/lib/rubocop/cop/util.rb +2 -2
  123. data/lib/rubocop/cop/variable_force/assignment.rb +1 -1
  124. data/lib/rubocop/cop/variable_force/variable_table.rb +1 -1
  125. data/lib/rubocop/cop/variable_force.rb +20 -29
  126. data/lib/rubocop/cops_documentation_generator.rb +2 -1
  127. data/lib/rubocop/ext/processed_source.rb +2 -0
  128. data/lib/rubocop/formatter/disabled_config_formatter.rb +25 -8
  129. data/lib/rubocop/formatter/html_formatter.rb +1 -1
  130. data/lib/rubocop/formatter/offense_count_formatter.rb +8 -5
  131. data/lib/rubocop/formatter/worst_offenders_formatter.rb +6 -3
  132. data/lib/rubocop/formatter.rb +3 -1
  133. data/lib/rubocop/optimized_patterns.rb +38 -0
  134. data/lib/rubocop/options.rb +28 -16
  135. data/lib/rubocop/path_util.rb +14 -2
  136. data/lib/rubocop/result_cache.rb +1 -1
  137. data/lib/rubocop/rspec/cop_helper.rb +24 -1
  138. data/lib/rubocop/rspec/shared_contexts.rb +14 -1
  139. data/lib/rubocop/rspec/support.rb +2 -2
  140. data/lib/rubocop/runner.rb +15 -11
  141. data/lib/rubocop/server/cache.rb +5 -1
  142. data/lib/rubocop/server/cli.rb +9 -2
  143. data/lib/rubocop/server/client_command/exec.rb +5 -0
  144. data/lib/rubocop/server/core.rb +19 -2
  145. data/lib/rubocop/server/socket_reader.rb +5 -1
  146. data/lib/rubocop/server.rb +1 -1
  147. data/lib/rubocop/target_ruby.rb +1 -1
  148. data/lib/rubocop/version.rb +8 -3
  149. data/lib/rubocop.rb +18 -6
  150. metadata +18 -5
@@ -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)
@@ -39,9 +39,10 @@ module RuboCop
39
39
  def autocorrect(node)
40
40
  return correct_elsif(node) if node.else_branch.if_type?
41
41
 
42
+ then_code = node.if_branch ? node.if_branch.source : 'nil'
42
43
  else_code = node.else_branch ? node.else_branch.source : 'nil'
43
44
 
44
- "#{node.condition.source} ? #{node.if_branch.source} : #{else_code}"
45
+ "#{node.condition.source} ? #{then_code} : #{else_code}"
45
46
  end
46
47
 
47
48
  def correct_elsif(node)
@@ -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))
@@ -44,7 +44,7 @@ module RuboCop
44
44
  PATTERN
45
45
 
46
46
  def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
47
- return unless node.lambda? || node.proc?
47
+ return unless node.lambda_or_proc?
48
48
  return unless nil_return?(node.body)
49
49
 
50
50
  message = format(MSG, type: node.lambda? ? 'lambda' : 'proc')
@@ -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
@@ -13,6 +13,7 @@ module RuboCop
13
13
  # ----
14
14
  # Methods:
15
15
  # join: ''
16
+ # sum: 0
16
17
  # split: ' '
17
18
  # chomp: "\n"
18
19
  # chomp!: "\n"
@@ -33,6 +34,7 @@ module RuboCop
33
34
  # # bad
34
35
  # array.join('')
35
36
  # [1, 2, 3].join("")
37
+ # array.sum(0)
36
38
  # string.split(" ")
37
39
  # "first\nsecond".split(" ")
38
40
  # string.chomp("\n")
@@ -42,6 +44,7 @@ module RuboCop
42
44
  # # good
43
45
  # array.join
44
46
  # [1, 2, 3].join
47
+ # array.sum
45
48
  # string.split
46
49
  # "first second".split
47
50
  # string.chomp
@@ -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,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Avoid redundant `::` prefix on constant.
7
+ #
8
+ # How Ruby searches constant is a bit complicated, and it can often be difficult to
9
+ # understand from the code whether the `::` is intended or not. Where `Module.nesting`
10
+ # is empty, there is no need to prepend `::`, so it would be nice to consistently
11
+ # avoid such meaningless `::` prefix to avoid confusion.
12
+ #
13
+ # @example
14
+ # # bad
15
+ # ::Const
16
+ #
17
+ # # good
18
+ # Const
19
+ #
20
+ # # bad
21
+ # class << self
22
+ # ::Const
23
+ # end
24
+ #
25
+ # # good
26
+ # class << self
27
+ # Const
28
+ # end
29
+ #
30
+ # # good
31
+ # class A
32
+ # ::Const
33
+ # end
34
+ #
35
+ # # good
36
+ # module A
37
+ # ::Const
38
+ # end
39
+ class RedundantConstantBase < Base
40
+ extend AutoCorrector
41
+
42
+ MSG = 'Remove redundant `::`.'
43
+
44
+ def on_cbase(node)
45
+ return unless bad?(node)
46
+
47
+ add_offense(node) do |corrector|
48
+ corrector.remove(node)
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def bad?(node)
55
+ module_nesting_ancestors_of(node).none?
56
+ end
57
+
58
+ def module_nesting_ancestors_of(node)
59
+ node.each_ancestor(:class, :module).reject do |ancestor|
60
+ ancestor.class_type? && used_in_super_class_part?(node, class_node: ancestor)
61
+ end
62
+ end
63
+
64
+ def used_in_super_class_part?(node, class_node:)
65
+ class_node.parent_class&.each_descendant(:cbase)&.any? do |descendant|
66
+ descendant.equal?(node)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ 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)
@@ -74,7 +74,7 @@ module RuboCop
74
74
 
75
75
  non_redundant =
76
76
  whitespace_in_free_space_mode?(node, class_elem) ||
77
- backslash_b?(class_elem) ||
77
+ backslash_b?(class_elem) || backslash_zero?(class_elem) ||
78
78
  requires_escape_outside_char_class?(class_elem)
79
79
 
80
80
  !non_redundant
@@ -104,6 +104,13 @@ module RuboCop
104
104
  elem == '\b'
105
105
  end
106
106
 
107
+ def backslash_zero?(elem)
108
+ # See https://github.com/rubocop/rubocop/issues/11067 for details - in short "\0" != "0" -
109
+ # the former means an Unicode code point `"\u0000"`, the latter a number character `"0"`.
110
+ # Similarly "\032" means "\u001A". Other numbers starting with "\0" can also be mentioned.
111
+ elem == '\0'
112
+ end
113
+
107
114
  def requires_escape_outside_char_class?(elem)
108
115
  REQUIRES_ESCAPE_OUTSIDE_CHAR_CLASS_CHARS.include?(elem)
109
116
  end