rubocop 1.39.0 → 1.42.0

Sign up to get free protection for your applications and to get access to all the features.
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