rubocop 1.39.0 → 1.42.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 (145) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +2 -2
  4. data/config/default.yml +91 -10
  5. data/exe/rubocop +1 -1
  6. data/lib/rubocop/cli.rb +1 -1
  7. data/lib/rubocop/comment_config.rb +5 -0
  8. data/lib/rubocop/config.rb +39 -15
  9. data/lib/rubocop/config_loader.rb +14 -5
  10. data/lib/rubocop/config_loader_resolver.rb +6 -2
  11. data/lib/rubocop/config_validator.rb +1 -1
  12. data/lib/rubocop/cop/badge.rb +9 -4
  13. data/lib/rubocop/cop/base.rb +84 -74
  14. data/lib/rubocop/cop/commissioner.rb +8 -3
  15. data/lib/rubocop/cop/cop.rb +29 -29
  16. data/lib/rubocop/cop/corrector.rb +23 -11
  17. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +22 -6
  18. data/lib/rubocop/cop/gemspec/dependency_version.rb +16 -18
  19. data/lib/rubocop/cop/internal_affairs/cop_description.rb +3 -1
  20. data/lib/rubocop/cop/internal_affairs/lambda_or_proc.rb +46 -0
  21. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  22. data/lib/rubocop/cop/layout/class_structure.rb +32 -11
  23. data/lib/rubocop/cop/layout/comment_indentation.rb +3 -1
  24. data/lib/rubocop/cop/layout/empty_lines.rb +2 -0
  25. data/lib/rubocop/cop/layout/extra_spacing.rb +10 -6
  26. data/lib/rubocop/cop/layout/first_array_element_line_break.rb +38 -2
  27. data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +49 -2
  28. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +61 -2
  29. data/lib/rubocop/cop/layout/first_method_parameter_line_break.rb +52 -2
  30. data/lib/rubocop/cop/layout/indentation_style.rb +7 -2
  31. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +5 -0
  32. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +11 -5
  33. data/lib/rubocop/cop/layout/line_length.rb +2 -0
  34. data/lib/rubocop/cop/layout/multiline_array_line_breaks.rb +51 -2
  35. data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -1
  36. data/lib/rubocop/cop/layout/multiline_hash_key_line_breaks.rb +49 -2
  37. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +53 -2
  38. data/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb +58 -2
  39. data/lib/rubocop/cop/layout/redundant_line_break.rb +2 -2
  40. data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
  41. data/lib/rubocop/cop/layout/trailing_empty_lines.rb +1 -1
  42. data/lib/rubocop/cop/layout/trailing_whitespace.rb +11 -4
  43. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  44. data/lib/rubocop/cop/lint/assignment_in_condition.rb +11 -1
  45. data/lib/rubocop/cop/lint/constant_resolution.rb +4 -0
  46. data/lib/rubocop/cop/lint/debugger.rb +3 -1
  47. data/lib/rubocop/cop/lint/deprecated_constants.rb +8 -1
  48. data/lib/rubocop/cop/lint/duplicate_branch.rb +0 -2
  49. data/lib/rubocop/cop/lint/duplicate_methods.rb +19 -8
  50. data/lib/rubocop/cop/lint/empty_block.rb +1 -5
  51. data/lib/rubocop/cop/lint/empty_conditional_body.rb +1 -1
  52. data/lib/rubocop/cop/lint/interpolation_check.rb +4 -3
  53. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +10 -5
  54. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +19 -0
  55. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +5 -0
  56. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +15 -3
  57. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +1 -1
  58. data/lib/rubocop/cop/lint/regexp_as_condition.rb +6 -0
  59. data/lib/rubocop/cop/lint/require_parentheses.rb +3 -1
  60. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +10 -12
  61. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +5 -4
  62. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +4 -3
  63. data/lib/rubocop/cop/lint/unused_method_argument.rb +2 -1
  64. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +5 -3
  65. data/lib/rubocop/cop/lint/void.rb +6 -6
  66. data/lib/rubocop/cop/metrics/block_length.rb +9 -4
  67. data/lib/rubocop/cop/metrics/class_length.rb +10 -5
  68. data/lib/rubocop/cop/metrics/method_length.rb +9 -4
  69. data/lib/rubocop/cop/metrics/module_length.rb +10 -5
  70. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
  71. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +4 -4
  72. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +6 -3
  73. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  74. data/lib/rubocop/cop/mixin/allowed_identifiers.rb +2 -2
  75. data/lib/rubocop/cop/mixin/annotation_comment.rb +13 -6
  76. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +21 -9
  77. data/lib/rubocop/cop/mixin/first_element_line_break.rb +11 -7
  78. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +28 -5
  79. data/lib/rubocop/cop/mixin/line_length_help.rb +8 -1
  80. data/lib/rubocop/cop/mixin/method_complexity.rb +5 -3
  81. data/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb +5 -3
  82. data/lib/rubocop/cop/mixin/percent_array.rb +3 -5
  83. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +1 -1
  84. data/lib/rubocop/cop/mixin/require_library.rb +2 -0
  85. data/lib/rubocop/cop/mixin/rescue_node.rb +3 -3
  86. data/lib/rubocop/cop/mixin/statement_modifier.rb +15 -1
  87. data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
  88. data/lib/rubocop/cop/naming/class_and_module_camel_case.rb +2 -0
  89. data/lib/rubocop/cop/naming/inclusive_language.rb +4 -1
  90. data/lib/rubocop/cop/registry.rb +51 -36
  91. data/lib/rubocop/cop/security/compound_hash.rb +2 -1
  92. data/lib/rubocop/cop/style/alias.rb +9 -1
  93. data/lib/rubocop/cop/style/array_intersect.rb +111 -0
  94. data/lib/rubocop/cop/style/block_comments.rb +1 -1
  95. data/lib/rubocop/cop/style/concat_array_literals.rb +86 -0
  96. data/lib/rubocop/cop/style/documentation.rb +11 -5
  97. data/lib/rubocop/cop/style/guard_clause.rb +44 -9
  98. data/lib/rubocop/cop/style/hash_syntax.rb +10 -7
  99. data/lib/rubocop/cop/style/identical_conditional_branches.rb +15 -0
  100. data/lib/rubocop/cop/style/if_with_semicolon.rb +4 -4
  101. data/lib/rubocop/cop/style/inverse_methods.rb +2 -0
  102. data/lib/rubocop/cop/style/line_end_concatenation.rb +4 -1
  103. data/lib/rubocop/cop/style/map_to_set.rb +61 -0
  104. data/lib/rubocop/cop/style/method_def_parentheses.rb +11 -4
  105. data/lib/rubocop/cop/style/min_max_comparison.rb +73 -0
  106. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  107. data/lib/rubocop/cop/style/redundant_argument.rb +3 -0
  108. data/lib/rubocop/cop/style/redundant_constant_base.rb +85 -0
  109. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +39 -0
  110. data/lib/rubocop/cop/style/redundant_return.rb +7 -0
  111. data/lib/rubocop/cop/style/redundant_sort.rb +1 -1
  112. data/lib/rubocop/cop/style/redundant_string_escape.rb +6 -3
  113. data/lib/rubocop/cop/style/require_order.rb +142 -0
  114. data/lib/rubocop/cop/style/safe_navigation.rb +35 -6
  115. data/lib/rubocop/cop/style/select_by_regexp.rb +13 -5
  116. data/lib/rubocop/cop/style/semicolon.rb +2 -1
  117. data/lib/rubocop/cop/style/signal_exception.rb +8 -6
  118. data/lib/rubocop/cop/style/string_literals.rb +1 -5
  119. data/lib/rubocop/cop/style/symbol_proc.rb +2 -4
  120. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +4 -4
  121. data/lib/rubocop/cop/style/word_array.rb +41 -0
  122. data/lib/rubocop/cop/style/yoda_expression.rb +74 -0
  123. data/lib/rubocop/cop/style/zero_length_predicate.rb +31 -14
  124. data/lib/rubocop/cop/team.rb +30 -30
  125. data/lib/rubocop/cop/util.rb +32 -5
  126. data/lib/rubocop/cop/variable_force/assignment.rb +1 -1
  127. data/lib/rubocop/cop/variable_force.rb +17 -29
  128. data/lib/rubocop/cops_documentation_generator.rb +33 -11
  129. data/lib/rubocop/directive_comment.rb +1 -1
  130. data/lib/rubocop/file_patterns.rb +43 -0
  131. data/lib/rubocop/formatter/disabled_config_formatter.rb +17 -6
  132. data/lib/rubocop/formatter/html_formatter.rb +1 -1
  133. data/lib/rubocop/formatter.rb +5 -1
  134. data/lib/rubocop/options.rb +8 -0
  135. data/lib/rubocop/path_util.rb +39 -16
  136. data/lib/rubocop/result_cache.rb +2 -2
  137. data/lib/rubocop/rspec/cop_helper.rb +4 -1
  138. data/lib/rubocop/rspec/support.rb +2 -2
  139. data/lib/rubocop/runner.rb +10 -3
  140. data/lib/rubocop/server/core.rb +1 -1
  141. data/lib/rubocop/target_finder.rb +1 -1
  142. data/lib/rubocop/target_ruby.rb +1 -2
  143. data/lib/rubocop/version.rb +1 -1
  144. data/lib/rubocop.rb +19 -6
  145. metadata +19 -9
@@ -136,6 +136,7 @@ module RuboCop
136
136
 
137
137
  private
138
138
 
139
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
139
140
  def check_branches(node, branches)
140
141
  # return if any branch is empty. An empty branch can be an `if`
141
142
  # without an `else` or a branch that contains only comments.
@@ -144,9 +145,13 @@ module RuboCop
144
145
  tails = branches.map { |branch| tail(branch) }
145
146
  check_expressions(node, tails, :after_condition) if duplicated_expressions?(node, tails)
146
147
 
148
+ return if last_child_of_parent?(node) &&
149
+ branches.any? { |branch| single_child_branch?(branch) }
150
+
147
151
  heads = branches.map { |branch| head(branch) }
148
152
  check_expressions(node, heads, :before_condition) if duplicated_expressions?(node, heads)
149
153
  end
154
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
150
155
 
151
156
  def duplicated_expressions?(node, expressions)
152
157
  unique_expressions = expressions.uniq
@@ -180,6 +185,16 @@ module RuboCop
180
185
  end
181
186
  end
182
187
 
188
+ def last_child_of_parent?(node)
189
+ return true unless (parent = node.parent)
190
+
191
+ parent.child_nodes.last == node
192
+ end
193
+
194
+ def single_child_branch?(branch_node)
195
+ !branch_node.begin_type? || branch_node.children.size == 1
196
+ end
197
+
183
198
  def message(node)
184
199
  format(MSG, source: node.source)
185
200
  end
@@ -21,13 +21,12 @@ module RuboCop
21
21
  MSG_TERNARY = 'Do not use `if %<expr>s;` - use a ternary operator instead.'
22
22
 
23
23
  def on_normal_if_unless(node)
24
- return unless node.else_branch
25
24
  return if node.parent&.if_type?
26
25
 
27
26
  beginning = node.loc.begin
28
27
  return unless beginning&.is?(';')
29
28
 
30
- message = node.else_branch.if_type? ? MSG_IF_ELSE : MSG_TERNARY
29
+ message = node.else_branch&.if_type? ? MSG_IF_ELSE : MSG_TERNARY
31
30
 
32
31
  add_offense(node, message: format(message, expr: node.condition.source)) do |corrector|
33
32
  corrector.replace(node, autocorrect(node))
@@ -37,11 +36,12 @@ module RuboCop
37
36
  private
38
37
 
39
38
  def autocorrect(node)
40
- return correct_elsif(node) if node.else_branch.if_type?
39
+ return correct_elsif(node) if node.else_branch&.if_type?
41
40
 
41
+ then_code = node.if_branch ? node.if_branch.source : 'nil'
42
42
  else_code = node.else_branch ? node.else_branch.source : 'nil'
43
43
 
44
- "#{node.condition.source} ? #{node.if_branch.source} : #{else_code}"
44
+ "#{node.condition.source} ? #{then_code} : #{else_code}"
45
45
  end
46
46
 
47
47
  def correct_elsif(node)
@@ -51,6 +51,8 @@ module RuboCop
51
51
  NEGATED_EQUALITY_METHODS = %i[!= !~].freeze
52
52
  CAMEL_CASE = /[A-Z]+[a-z]+/.freeze
53
53
 
54
+ RESTRICT_ON_SEND = [:!].freeze
55
+
54
56
  def self.autocorrect_incompatible_with
55
57
  [Style::Not, Style::SymbolProc]
56
58
  end
@@ -55,7 +55,10 @@ module RuboCop
55
55
  private
56
56
 
57
57
  def check_token_set(index)
58
- predecessor, operator, successor = processed_source.tokens[index, 3]
58
+ tokens = processed_source.tokens
59
+ predecessor = tokens[index]
60
+ operator = tokens[index + 1]
61
+ successor = tokens[index + 2]
59
62
 
60
63
  return unless eligible_token_set?(predecessor, operator, successor)
61
64
 
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Looks for uses of `map.to_set` or `collect.to_set` that could be
7
+ # written with just `to_set`.
8
+ #
9
+ # @safety
10
+ # This cop is unsafe, as it can produce false positives if the receiver
11
+ # is not an `Enumerable`.
12
+ #
13
+ # @example
14
+ # # bad
15
+ # something.map { |i| i * 2 }.to_set
16
+ #
17
+ # # good
18
+ # something.to_set { |i| i * 2 }
19
+ #
20
+ # # bad
21
+ # [1, 2, 3].collect { |i| i.to_s }.to_set
22
+ #
23
+ # # good
24
+ # [1, 2, 3].to_set { |i| i.to_s }
25
+ #
26
+ class MapToSet < Base
27
+ extend AutoCorrector
28
+ include RangeHelp
29
+
30
+ MSG = 'Pass a block to `to_set` instead of calling `%<method>s.to_set`.'
31
+ RESTRICT_ON_SEND = %i[to_set].freeze
32
+
33
+ # @!method map_to_set?(node)
34
+ def_node_matcher :map_to_set?, <<~PATTERN
35
+ $(send (block $(send _ {:map :collect}) ...) :to_set)
36
+ PATTERN
37
+
38
+ def on_send(node)
39
+ return unless (to_set_node, map_node = map_to_set?(node))
40
+
41
+ message = format(MSG, method: map_node.loc.selector.source)
42
+ add_offense(map_node.loc.selector, message: message) do |corrector|
43
+ # If the `to_set` call already has a block, do not autocorrect.
44
+ next if to_set_node.block_node
45
+
46
+ autocorrect(corrector, to_set_node, map_node)
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def autocorrect(corrector, to_set, map)
53
+ removal_range = range_between(to_set.loc.dot.begin_pos, to_set.loc.selector.end_pos)
54
+
55
+ corrector.remove(range_with_surrounding_space(removal_range, side: :left))
56
+ corrector.replace(map.loc.selector, 'to_set')
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -10,7 +10,9 @@ module RuboCop
10
10
  #
11
11
  # 1. Endless methods
12
12
  # 2. Argument lists containing a `forward-arg` (`...`)
13
- # 3. Argument lists containing an anonymous block forwarding (`&`)
13
+ # 3. Argument lists containing an anonymous rest arguments forwarding (`*`)
14
+ # 4. Argument lists containing an anonymous keyword rest arguments forwarding (`**`)
15
+ # 5. Argument lists containing an anonymous block forwarding (`&`)
14
16
  #
15
17
  # Removing the parens would be a syntax error here.
16
18
  #
@@ -130,9 +132,11 @@ module RuboCop
130
132
  # Regardless of style, parentheses are necessary for:
131
133
  # 1. Endless methods
132
134
  # 2. Argument lists containing a `forward-arg` (`...`)
133
- # 3. Argument lists containing an anonymous block forwarding (`&`)
135
+ # 3. Argument lists containing an anonymous rest arguments forwarding (`*`)
136
+ # 4. Argument lists containing an anonymous keyword rest arguments forwarding (`**`)
137
+ # 5. Argument lists containing an anonymous block forwarding (`&`)
134
138
  # Removing the parens would be a syntax error here.
135
- node.endless? || node.arguments.any?(&:forward_arg_type?) || anonymous_block_arg?(node)
139
+ node.endless? || anonymous_arguments?(node)
136
140
  end
137
141
 
138
142
  def require_parentheses?(args)
@@ -162,7 +166,10 @@ module RuboCop
162
166
  end
163
167
  end
164
168
 
165
- def anonymous_block_arg?(node)
169
+ def anonymous_arguments?(node)
170
+ return true if node.arguments.any? do |arg|
171
+ arg.forward_arg_type? || arg.restarg_type? || arg.kwrestarg_type?
172
+ end
166
173
  return false unless (last_argument = node.arguments.last)
167
174
 
168
175
  last_argument.blockarg_type? && last_argument.name.nil?
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Enforces the use of `max` or `min` instead of comparison for greater or less.
7
+ #
8
+ # NOTE: It can be used if you want to present limit or threshold in Ruby 2.7+.
9
+ # That it is slow though. So autocorrection will apply generic `max` or `min`:
10
+ #
11
+ # [source,ruby]
12
+ # ----
13
+ # a.clamp(b..) # Same as `[a, b].max`
14
+ # a.clamp(..b) # Same as `[a, b].min`
15
+ # ----
16
+ #
17
+ # @safety
18
+ # This cop is unsafe because even if a value has `<` or `>` method,
19
+ # it is not necessarily `Comparable`.
20
+ #
21
+ # @example
22
+ #
23
+ # # bad
24
+ # a > b ? a : b
25
+ # a >= b ? a : b
26
+ #
27
+ # # good
28
+ # [a, b].max
29
+ #
30
+ # # bad
31
+ # a < b ? a : b
32
+ # a <= b ? a : b
33
+ #
34
+ # # good
35
+ # [a, b].min
36
+ #
37
+ class MinMaxComparison < Base
38
+ extend AutoCorrector
39
+
40
+ MSG = 'Use `%<prefer>s` instead.'
41
+ GRATER_OPERATORS = %i[> >=].freeze
42
+ LESS_OPERATORS = %i[< <=].freeze
43
+ COMPARISON_OPERATORS = GRATER_OPERATORS + LESS_OPERATORS
44
+
45
+ def on_if(node)
46
+ lhs, operator, rhs = *node.condition
47
+ return unless COMPARISON_OPERATORS.include?(operator)
48
+
49
+ if_branch = node.if_branch
50
+ else_branch = node.else_branch
51
+ preferred_method = preferred_method(operator, lhs, rhs, if_branch, else_branch)
52
+ return unless preferred_method
53
+
54
+ replacement = "[#{lhs.source}, #{rhs.source}].#{preferred_method}"
55
+
56
+ add_offense(node, message: format(MSG, prefer: replacement)) do |corrector|
57
+ corrector.replace(node, replacement)
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def preferred_method(operator, lhs, rhs, if_branch, else_branch)
64
+ if lhs == if_branch && rhs == else_branch
65
+ GRATER_OPERATORS.include?(operator) ? 'max' : 'min'
66
+ elsif lhs == else_branch && rhs == if_branch
67
+ LESS_OPERATORS.include?(operator) ? 'max' : 'min'
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -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')
@@ -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
@@ -0,0 +1,85 @@
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
+ # NOTE: This cop is disabled if `Lint/ConstantResolution` cop is enabled to prevent
14
+ # conflicting rules. Because it respects user configurations that want to enable
15
+ # `Lint/ConstantResolution` cop which is disabled by default.
16
+ #
17
+ # @example
18
+ # # bad
19
+ # ::Const
20
+ #
21
+ # # good
22
+ # Const
23
+ #
24
+ # # bad
25
+ # class << self
26
+ # ::Const
27
+ # end
28
+ #
29
+ # # good
30
+ # class << self
31
+ # Const
32
+ # end
33
+ #
34
+ # # good
35
+ # class A
36
+ # ::Const
37
+ # end
38
+ #
39
+ # # good
40
+ # module A
41
+ # ::Const
42
+ # end
43
+ class RedundantConstantBase < Base
44
+ extend AutoCorrector
45
+
46
+ MSG = 'Remove redundant `::`.'
47
+
48
+ def on_cbase(node)
49
+ return if lint_constant_resolution_cop_enabled?
50
+ return unless bad?(node)
51
+
52
+ add_offense(node) do |corrector|
53
+ corrector.remove(node)
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def lint_constant_resolution_cop_enabled?
60
+ lint_constant_resolution_config.fetch('Enabled', false)
61
+ end
62
+
63
+ def lint_constant_resolution_config
64
+ config.for_cop('Lint/ConstantResolution')
65
+ end
66
+
67
+ def bad?(node)
68
+ module_nesting_ancestors_of(node).none?
69
+ end
70
+
71
+ def module_nesting_ancestors_of(node)
72
+ node.each_ancestor(:class, :module).reject do |ancestor|
73
+ ancestor.class_type? && used_in_super_class_part?(node, class_node: ancestor)
74
+ end
75
+ end
76
+
77
+ def used_in_super_class_part?(node, class_node:)
78
+ class_node.parent_class&.each_descendant(:cbase)&.any? do |descendant|
79
+ descendant.equal?(node)
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for redundant uses of double splat hash braces.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # do_something(**{foo: bar, baz: qux})
12
+ #
13
+ # # good
14
+ # do_something(foo: bar, baz: qux)
15
+ #
16
+ class RedundantDoubleSplatHashBraces < Base
17
+ extend AutoCorrector
18
+
19
+ MSG = 'Remove the redundant double splat and braces, use keyword arguments directly.'
20
+
21
+ # @!method double_splat_hash_braces?(node)
22
+ def_node_matcher :double_splat_hash_braces?, <<~PATTERN
23
+ (hash (kwsplat (hash ...)))
24
+ PATTERN
25
+
26
+ def on_hash(node)
27
+ return if node.pairs.empty? || node.pairs.any?(&:hash_rocket?)
28
+
29
+ grandparent = node.parent&.parent
30
+ return unless double_splat_hash_braces?(grandparent)
31
+
32
+ add_offense(grandparent) do |corrector|
33
+ corrector.replace(grandparent, node.pairs.map(&:source).join(', '))
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -53,6 +53,13 @@ module RuboCop
53
53
 
54
54
  MSG = 'Redundant `return` detected.'
55
55
  MULTI_RETURN_MSG = 'To return multiple values, use an array.'
56
+ RESTRICT_ON_SEND = %i[define_method define_singleton_method].freeze
57
+
58
+ def on_send(node)
59
+ return unless (parent = node.parent) && parent.block_type?
60
+
61
+ check_branch(parent.body)
62
+ end
56
63
 
57
64
  def on_def(node)
58
65
  check_branch(node.body)
@@ -184,7 +184,7 @@ module RuboCop
184
184
  end
185
185
 
186
186
  def arg_value(node)
187
- arg_node(node).nil? ? nil : arg_node(node).node_parts.first
187
+ arg_node(node)&.node_parts&.first
188
188
  end
189
189
 
190
190
  # This gets the start of the accessor whether it has a dot
@@ -58,7 +58,7 @@ module RuboCop
58
58
  private
59
59
 
60
60
  def message(range)
61
- format(MSG, char: range.source.chars.last)
61
+ format(MSG, char: range.source[-1])
62
62
  end
63
63
 
64
64
  def str_contents_range(node)
@@ -76,6 +76,7 @@ module RuboCop
76
76
  node.loc.to_hash.key?(:begin) && !node.loc.begin.nil?
77
77
  end
78
78
 
79
+ # rubocop:disable Metrics/CyclomaticComplexity
79
80
  def allowed_escape?(node, range)
80
81
  escaped = range.source[(1..-1)]
81
82
 
@@ -88,13 +89,14 @@ module RuboCop
88
89
  # with different versions of Ruby so that e.g. /\d/ != /d/
89
90
  return true if /[\n\\[[:alnum:]]]/.match?(escaped[0])
90
91
 
91
- return true if escaped[0] == ' ' && percent_array_literal?(node)
92
+ return true if escaped[0] == ' ' && (percent_array_literal?(node) || node.heredoc?)
92
93
 
93
94
  return true if disabling_interpolation?(range)
94
95
  return true if delimiter?(node, escaped[0])
95
96
 
96
97
  false
97
98
  end
99
+ # rubocop:enable Metrics/CyclomaticComplexity
98
100
 
99
101
  def interpolation_not_enabled?(node)
100
102
  single_quoted?(node) ||
@@ -169,9 +171,10 @@ module RuboCop
169
171
  # Allow \#{foo}, \#$foo, \#@foo, and \#@@foo
170
172
  # for escaping local, global, instance and class variable interpolations
171
173
  return true if range.source.match?(/\A\\#[{$@]/)
172
-
173
174
  # Also allow #\{foo}, #\$foo, #\@foo and #\@@foo
174
175
  return true if range.adjust(begin_pos: -2).source.match?(/\A[^\\]#\\[{$@]/)
176
+ # For `\#\{foo} allow `\#` and warn `\{`
177
+ return true if range.adjust(end_pos: 1).source == '\\#\\{'
175
178
 
176
179
  false
177
180
  end
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Sort `require` and `require_relative` in alphabetical order.
7
+ #
8
+ # @safety
9
+ # This cop's autocorrection is unsafe because it will obviously change the execution order.
10
+ #
11
+ # @example
12
+ # # bad
13
+ # require 'b'
14
+ # require 'a'
15
+ #
16
+ # # good
17
+ # require 'a'
18
+ # require 'b'
19
+ #
20
+ # # bad
21
+ # require_relative 'b'
22
+ # require_relative 'a'
23
+ #
24
+ # # good
25
+ # require_relative 'a'
26
+ # require_relative 'b'
27
+ #
28
+ # # good (sorted within each section separated by a blank line)
29
+ # require 'a'
30
+ # require 'd'
31
+ #
32
+ # require 'b'
33
+ # require 'c'
34
+ #
35
+ # # good
36
+ # require 'b'
37
+ # require_relative 'c'
38
+ # require 'a'
39
+ #
40
+ # # bad
41
+ # require 'a'
42
+ # require 'c' if foo
43
+ # require 'b'
44
+ #
45
+ # # good
46
+ # require 'a'
47
+ # require 'b'
48
+ # require 'c' if foo
49
+ #
50
+ # # bad
51
+ # require 'c'
52
+ # if foo
53
+ # require 'd'
54
+ # require 'b'
55
+ # end
56
+ # require 'a'
57
+ #
58
+ # # good
59
+ # require 'c'
60
+ # if foo
61
+ # require 'b'
62
+ # require 'd'
63
+ # end
64
+ # require 'a'
65
+ #
66
+ class RequireOrder < Base
67
+ extend AutoCorrector
68
+
69
+ include RangeHelp
70
+
71
+ RESTRICT_ON_SEND = %i[require require_relative].freeze
72
+
73
+ MSG = 'Sort `%<name>s` in alphabetical order.'
74
+
75
+ # @!method if_inside_only_require(node)
76
+ def_node_matcher :if_inside_only_require, <<~PATTERN
77
+ {
78
+ (if _ _ $(send nil? {:require :require_relative} _))
79
+ (if _ $(send nil? {:require :require_relative} _) _)
80
+ }
81
+ PATTERN
82
+
83
+ def on_send(node)
84
+ return unless node.parent && node.arguments?
85
+ return if not_modifier_form?(node.parent)
86
+
87
+ previous_older_sibling = find_previous_older_sibling(node)
88
+ return unless previous_older_sibling
89
+
90
+ add_offense(node, message: format(MSG, name: node.method_name)) do |corrector|
91
+ swap(
92
+ range_with_comments_and_lines(previous_older_sibling),
93
+ range_with_comments_and_lines(node.parent.if_type? ? node.parent : node),
94
+ corrector: corrector
95
+ )
96
+ end
97
+ end
98
+
99
+ private
100
+
101
+ def not_modifier_form?(node)
102
+ node.if_type? && !node.modifier_form?
103
+ end
104
+
105
+ def find_previous_older_sibling(node) # rubocop:disable Metrics
106
+ search_node(node).left_siblings.reverse.find do |sibling|
107
+ next unless sibling.is_a?(AST::Node)
108
+
109
+ sibling = sibling_node(sibling)
110
+ break unless sibling&.send_type? && sibling&.method?(node.method_name)
111
+ break unless sibling.arguments? && !sibling.receiver
112
+ break unless in_same_section?(sibling, node)
113
+
114
+ node.first_argument.source < sibling.first_argument.source
115
+ end
116
+ end
117
+
118
+ def search_node(node)
119
+ node.parent.if_type? ? node.parent : node
120
+ end
121
+
122
+ def sibling_node(node)
123
+ return if not_modifier_form?(node)
124
+
125
+ node.if_type? ? if_inside_only_require(node) : node
126
+ end
127
+
128
+ def in_same_section?(node1, node2)
129
+ !node1.location.expression.with(
130
+ end_pos: node2.location.expression.end_pos
131
+ ).source.include?("\n\n")
132
+ end
133
+
134
+ def swap(range1, range2, corrector:)
135
+ inserted = range2.source
136
+ corrector.insert_before(range1, inserted)
137
+ corrector.remove(range2)
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end