rubocop 1.75.1 → 1.76.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 (132) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +19 -13
  3. data/config/default.yml +79 -23
  4. data/config/obsoletion.yml +6 -3
  5. data/lib/rubocop/config_validator.rb +6 -6
  6. data/lib/rubocop/cop/autocorrect_logic.rb +18 -10
  7. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -1
  8. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +50 -6
  9. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
  10. data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
  11. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +2 -0
  12. data/lib/rubocop/cop/internal_affairs/undefined_config.rb +6 -1
  13. data/lib/rubocop/cop/layout/block_alignment.rb +1 -2
  14. data/lib/rubocop/cop/layout/class_structure.rb +35 -0
  15. data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
  16. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
  17. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +2 -2
  18. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +6 -2
  19. data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -1
  20. data/lib/rubocop/cop/layout/hash_alignment.rb +2 -2
  21. data/lib/rubocop/cop/layout/leading_comment_space.rb +13 -1
  22. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +1 -1
  23. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +2 -4
  24. data/lib/rubocop/cop/layout/space_after_semicolon.rb +10 -0
  25. data/lib/rubocop/cop/layout/space_before_brackets.rb +6 -32
  26. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +5 -1
  27. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +3 -0
  28. data/lib/rubocop/cop/lint/array_literal_in_regexp.rb +2 -3
  29. data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -1
  30. data/lib/rubocop/cop/lint/circular_argument_reference.rb +2 -5
  31. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +1 -1
  32. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +1 -1
  33. data/lib/rubocop/cop/lint/duplicate_methods.rb +86 -5
  34. data/lib/rubocop/cop/lint/empty_interpolation.rb +3 -1
  35. data/lib/rubocop/cop/lint/float_comparison.rb +27 -0
  36. data/lib/rubocop/cop/lint/identity_comparison.rb +19 -15
  37. data/lib/rubocop/cop/lint/literal_as_condition.rb +31 -25
  38. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +1 -1
  39. data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
  40. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +2 -2
  41. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +1 -1
  42. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +7 -4
  43. data/lib/rubocop/cop/lint/return_in_void_context.rb +7 -2
  44. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +5 -0
  45. data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
  46. data/lib/rubocop/cop/lint/to_enum_arguments.rb +1 -1
  47. data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +1 -1
  48. data/lib/rubocop/cop/lint/useless_assignment.rb +2 -0
  49. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +87 -0
  50. data/lib/rubocop/cop/lint/useless_or.rb +98 -0
  51. data/lib/rubocop/cop/lint/useless_rescue.rb +1 -1
  52. data/lib/rubocop/cop/lint/void.rb +2 -2
  53. data/lib/rubocop/cop/message_annotator.rb +7 -3
  54. data/lib/rubocop/cop/metrics/abc_size.rb +1 -1
  55. data/lib/rubocop/cop/mixin/check_line_breakable.rb +2 -2
  56. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +1 -1
  57. data/lib/rubocop/cop/mixin/def_node.rb +1 -1
  58. data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +1 -1
  59. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +0 -1
  60. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +15 -14
  61. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +2 -0
  62. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
  63. data/lib/rubocop/cop/mixin/trailing_comma.rb +9 -5
  64. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
  65. data/lib/rubocop/cop/naming/method_name.rb +1 -1
  66. data/lib/rubocop/cop/naming/predicate_method.rb +216 -0
  67. data/lib/rubocop/cop/naming/{predicate_name.rb → predicate_prefix.rb} +2 -2
  68. data/lib/rubocop/cop/style/access_modifier_declarations.rb +32 -10
  69. data/lib/rubocop/cop/style/arguments_forwarding.rb +8 -5
  70. data/lib/rubocop/cop/style/class_and_module_children.rb +19 -3
  71. data/lib/rubocop/cop/style/class_equality_comparison.rb +1 -1
  72. data/lib/rubocop/cop/style/command_literal.rb +1 -1
  73. data/lib/rubocop/cop/style/commented_keyword.rb +2 -2
  74. data/lib/rubocop/cop/style/comparable_between.rb +5 -2
  75. data/lib/rubocop/cop/style/conditional_assignment.rb +16 -4
  76. data/lib/rubocop/cop/style/data_inheritance.rb +7 -0
  77. data/lib/rubocop/cop/style/def_with_parentheses.rb +18 -5
  78. data/lib/rubocop/cop/style/double_negation.rb +1 -1
  79. data/lib/rubocop/cop/style/empty_literal.rb +4 -0
  80. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +100 -0
  81. data/lib/rubocop/cop/style/eval_with_location.rb +3 -3
  82. data/lib/rubocop/cop/style/explicit_block_argument.rb +2 -2
  83. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +3 -2
  84. data/lib/rubocop/cop/style/global_std_stream.rb +3 -0
  85. data/lib/rubocop/cop/style/hash_fetch_chain.rb +0 -1
  86. data/lib/rubocop/cop/style/hash_syntax.rb +3 -0
  87. data/lib/rubocop/cop/style/hash_transform_keys.rb +2 -2
  88. data/lib/rubocop/cop/style/hash_transform_values.rb +2 -2
  89. data/lib/rubocop/cop/style/identical_conditional_branches.rb +3 -3
  90. data/lib/rubocop/cop/style/if_unless_modifier.rb +22 -4
  91. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +4 -7
  92. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +1 -1
  93. data/lib/rubocop/cop/style/it_block_parameter.rb +33 -14
  94. data/lib/rubocop/cop/style/keyword_parameters_order.rb +1 -1
  95. data/lib/rubocop/cop/style/lambda_call.rb +7 -2
  96. data/lib/rubocop/cop/style/map_into_array.rb +3 -1
  97. data/lib/rubocop/cop/style/map_to_hash.rb +11 -0
  98. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +5 -2
  99. data/lib/rubocop/cop/style/multiline_if_modifier.rb +2 -0
  100. data/lib/rubocop/cop/style/percent_q_literals.rb +1 -1
  101. data/lib/rubocop/cop/style/redundant_array_flatten.rb +48 -0
  102. data/lib/rubocop/cop/style/redundant_condition.rb +13 -1
  103. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +1 -1
  104. data/lib/rubocop/cop/style/redundant_format.rb +6 -1
  105. data/lib/rubocop/cop/style/redundant_line_continuation.rb +0 -3
  106. data/lib/rubocop/cop/style/redundant_parentheses.rb +31 -3
  107. data/lib/rubocop/cop/style/regexp_literal.rb +1 -1
  108. data/lib/rubocop/cop/style/return_nil.rb +2 -2
  109. data/lib/rubocop/cop/style/safe_navigation.rb +28 -10
  110. data/lib/rubocop/cop/style/sole_nested_conditional.rb +4 -2
  111. data/lib/rubocop/cop/style/string_concatenation.rb +1 -2
  112. data/lib/rubocop/cop/style/struct_inheritance.rb +8 -1
  113. data/lib/rubocop/cop/style/super_arguments.rb +1 -2
  114. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +7 -1
  115. data/lib/rubocop/cop/style/trailing_comma_in_array_literal.rb +1 -1
  116. data/lib/rubocop/cop/style/trailing_comma_in_hash_literal.rb +1 -1
  117. data/lib/rubocop/cop/team.rb +1 -1
  118. data/lib/rubocop/cop/util.rb +1 -1
  119. data/lib/rubocop/cop/variable_force/assignment.rb +7 -3
  120. data/lib/rubocop/cop/variable_force/variable.rb +1 -1
  121. data/lib/rubocop/cops_documentation_generator.rb +6 -2
  122. data/lib/rubocop/formatter/disabled_config_formatter.rb +2 -1
  123. data/lib/rubocop/formatter/html_formatter.rb +1 -1
  124. data/lib/rubocop/formatter/pacman_formatter.rb +1 -1
  125. data/lib/rubocop/magic_comment.rb +8 -0
  126. data/lib/rubocop/rspec/cop_helper.rb +2 -2
  127. data/lib/rubocop/rspec/shared_contexts.rb +1 -2
  128. data/lib/rubocop/server/cache.rb +13 -10
  129. data/lib/rubocop/target_finder.rb +6 -2
  130. data/lib/rubocop/version.rb +1 -1
  131. data/lib/rubocop.rb +6 -1
  132. metadata +12 -7
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for empty strings being assigned inside string interpolation.
7
+ #
8
+ # Empty strings are a meaningless outcome inside of string interpolation, so we remove them.
9
+ # Alternatively, when configured to do so, we prioritise using empty strings.
10
+ #
11
+ # While this cop would also apply to variables that are only going to be used as strings,
12
+ # RuboCop can't detect that, so we only check inside of string interpolation.
13
+ #
14
+ # @example EnforcedStyle: trailing_conditional (default)
15
+ # # bad
16
+ # "#{condition ? 'foo' : ''}"
17
+ #
18
+ # # good
19
+ # "#{'foo' if condition}"
20
+ #
21
+ # # bad
22
+ # "#{condition ? '' : 'foo'}"
23
+ #
24
+ # # good
25
+ # "#{'foo' unless condition}"
26
+ #
27
+ # @example EnforcedStyle: ternary
28
+ # # bad
29
+ # "#{'foo' if condition}"
30
+ #
31
+ # # good
32
+ # "#{condition ? 'foo' : ''}"
33
+ #
34
+ # # bad
35
+ # "#{'foo' unless condition}"
36
+ #
37
+ # # good
38
+ # "#{condition ? '' : 'foo'}"
39
+ #
40
+ class EmptyStringInsideInterpolation < Base
41
+ include ConfigurableEnforcedStyle
42
+ include Interpolation
43
+ extend AutoCorrector
44
+
45
+ MSG_TRAILING_CONDITIONAL = 'Do not use trailing conditionals in string interpolation.'
46
+ MSG_TERNARY = 'Do not return empty strings in string interpolation.'
47
+
48
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
49
+ def on_interpolation(node)
50
+ node.each_child_node(:if) do |child_node|
51
+ if style == :trailing_conditional
52
+ if empty_if_outcome?(child_node)
53
+ ternary_style_autocorrect(child_node, child_node.else_branch.source, 'unless')
54
+ end
55
+
56
+ if empty_else_outcome?(child_node)
57
+ ternary_style_autocorrect(child_node, child_node.if_branch.source, 'if')
58
+ end
59
+ elsif style == :ternary
60
+ next unless child_node.modifier_form?
61
+
62
+ ternary_component = if child_node.unless?
63
+ "'' : #{child_node.if_branch.source}"
64
+ else
65
+ "#{child_node.if_branch.source} : ''"
66
+ end
67
+
68
+ add_offense(node, message: MSG_TRAILING_CONDITIONAL) do |corrector|
69
+ corrector.replace(node, "\#{#{child_node.condition.source} ? #{ternary_component}}")
70
+ end
71
+ end
72
+ end
73
+ end
74
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
75
+
76
+ private
77
+
78
+ def empty_if_outcome?(node)
79
+ empty_branch_outcome?(node.if_branch)
80
+ end
81
+
82
+ def empty_else_outcome?(node)
83
+ empty_branch_outcome?(node.else_branch)
84
+ end
85
+
86
+ def empty_branch_outcome?(branch)
87
+ return false unless branch
88
+
89
+ branch.nil_type? || (branch.str_type? && branch.value.empty?)
90
+ end
91
+
92
+ def ternary_style_autocorrect(node, outcome, condition)
93
+ add_offense(node, message: MSG_TERNARY) do |corrector|
94
+ corrector.replace(node, "#{outcome} #{condition} #{node.condition.source}")
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -4,12 +4,12 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # Ensures that eval methods (`eval`, `instance_eval`, `class_eval`
7
- # and `module_eval`) are given filename and line number values (`\_\_FILE\_\_`
8
- # and `\_\_LINE\_\_`). This data is used to ensure that any errors raised
7
+ # and `module_eval`) are given filename and line number values (`+__FILE__+`
8
+ # and `+__LINE__+`). This data is used to ensure that any errors raised
9
9
  # within the evaluated code will be given the correct identification
10
10
  # in a backtrace.
11
11
  #
12
- # The cop also checks that the line number given relative to `\_\_LINE\_\_` is
12
+ # The cop also checks that the line number given relative to `+__LINE__+` is
13
13
  # correct.
14
14
  #
15
15
  # This cop will autocorrect incorrect or missing filename and line number
@@ -65,7 +65,7 @@ module RuboCop
65
65
  yielding_block?(block_node) do |send_node, block_args, yield_args|
66
66
  return unless yielding_arguments?(block_args, yield_args)
67
67
 
68
- def_node = block_node.each_ancestor(:def, :defs).first
68
+ def_node = block_node.each_ancestor(:any_def).first
69
69
  # if `yield` is being called outside of a method context, ignore
70
70
  # this is not a valid ruby pattern, but can happen in haml or erb,
71
71
  # so this can cause crashes in haml_lint
@@ -151,7 +151,7 @@ module RuboCop
151
151
  end
152
152
 
153
153
  def build_new_arguments_for_zsuper(node)
154
- def_node = node.each_ancestor(:def, :defs).first
154
+ def_node = node.each_ancestor(:any_def).first
155
155
  def_node.arguments.map do |arg|
156
156
  arg.optarg_type? ? arg.node_parts[0] : arg.source
157
157
  end
@@ -151,7 +151,7 @@ module RuboCop
151
151
 
152
152
  def frozen_string_literal_comment(processed_source)
153
153
  processed_source.tokens.find do |token|
154
- token.text.start_with?(FROZEN_STRING_LITERAL_REGEXP)
154
+ MagicComment.parse(token.text).frozen_string_literal_specified?
155
155
  end
156
156
  end
157
157
 
@@ -189,8 +189,9 @@ module RuboCop
189
189
 
190
190
  def enable_comment(corrector)
191
191
  comment = frozen_string_literal_comment(processed_source)
192
+ replacement = MagicComment.parse(comment.text).new_frozen_string_literal(true)
192
193
 
193
- corrector.replace(line_range(comment.line), FROZEN_STRING_LITERAL_ENABLED)
194
+ corrector.replace(line_range(comment.line), replacement)
194
195
  end
195
196
 
196
197
  def insert_comment(corrector)
@@ -8,6 +8,9 @@ module RuboCop
8
8
  # reassign (possibly to redirect some stream) constants in Ruby, you'll get
9
9
  # an interpreter warning if you do so.
10
10
  #
11
+ # Additionally, `$stdout/$stderr/$stdin` can safely be accessed in a Ractor because they
12
+ # are ractor-local, while `STDOUT/STDERR/STDIN` will raise `Ractor::IsolationError`.
13
+ #
11
14
  # @safety
12
15
  # Autocorrection is unsafe because `STDOUT` and `$stdout` may point to different
13
16
  # objects, for example.
@@ -40,7 +40,6 @@ module RuboCop
40
40
  class HashFetchChain < Base
41
41
  extend AutoCorrector
42
42
  extend TargetRubyVersion
43
- include IgnoredNode
44
43
 
45
44
  MSG = 'Use `%<replacement>s` instead.'
46
45
  RESTRICT_ON_SEND = %i[fetch].freeze
@@ -76,6 +76,9 @@ module RuboCop
76
76
  # # good
77
77
  # {foo:, bar:}
78
78
  #
79
+ # # good - allowed to mix syntaxes
80
+ # {foo:, bar: baz}
81
+ #
79
82
  # @example EnforcedShorthandSyntax: never
80
83
  #
81
84
  # # bad
@@ -3,8 +3,8 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Looks for uses of `\_.each_with_object({}) {...}`,
7
- # `\_.map {...}.to_h`, and `Hash[\_.map {...}]` that are actually just
6
+ # Looks for uses of `+_.each_with_object({}) {...}+`,
7
+ # `+_.map {...}.to_h+`, and `+Hash[_.map {...}]+` that are actually just
8
8
  # transforming the keys of a hash, and tries to use a simpler & faster
9
9
  # call to `transform_keys` instead.
10
10
  # It should only be enabled on Ruby version 2.5 or newer.
@@ -3,8 +3,8 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Looks for uses of `\_.each_with_object({}) {...}`,
7
- # `\_.map {...}.to_h`, and `Hash[\_.map {...}]` that are actually just
6
+ # Looks for uses of `+_.each_with_object({}) {...}+`,
7
+ # `+_.map {...}.to_h+`, and `+Hash[_.map {...}]+` that are actually just
8
8
  # transforming the values of a hash, and tries to use a simpler & faster
9
9
  # call to `transform_values` instead.
10
10
  #
@@ -189,7 +189,7 @@ module RuboCop
189
189
  end
190
190
  end
191
191
 
192
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
192
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
193
193
  def check_expressions(node, expressions, insert_position)
194
194
  return if expressions.any?(&:nil?)
195
195
 
@@ -197,7 +197,7 @@ module RuboCop
197
197
 
198
198
  expressions.each do |expression|
199
199
  add_offense(expression) do |corrector|
200
- next if node.if_type? && node.ternary?
200
+ next if node.if_type? && (node.ternary? || node.then?)
201
201
 
202
202
  range = range_by_whole_lines(expression.source_range, include_final_newline: true)
203
203
  corrector.remove(range)
@@ -213,7 +213,7 @@ module RuboCop
213
213
  end
214
214
  end
215
215
  end
216
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
216
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
217
217
 
218
218
  def correct_assignment(corrector, node, expression, insert_position)
219
219
  if insert_position == :after_condition
@@ -23,6 +23,17 @@ module RuboCop
23
23
  # end
24
24
  # ----
25
25
  #
26
+ # The code `def method_name = body if condition` is considered a bad case by
27
+ # `Style/AmbiguousEndlessMethodDefinition` cop. So, to respect the user's intention to use
28
+ # an endless method definition in the `if` body, the following code is allowed:
29
+ #
30
+ # [source,ruby]
31
+ # ----
32
+ # if condition
33
+ # def method_name = body
34
+ # end
35
+ # ----
36
+ #
26
37
  # NOTE: It is allowed when `defined?` argument has an undefined value,
27
38
  # because using the modifier form causes the following incompatibility:
28
39
  #
@@ -77,10 +88,14 @@ module RuboCop
77
88
  [Style::SoleNestedConditional]
78
89
  end
79
90
 
91
+ # rubocop:disable Metrics/AbcSize
80
92
  def on_if(node)
93
+ return if endless_method?(node.body)
94
+
81
95
  condition = node.condition
82
96
  return if defined_nodes(condition).any? { |n| defined_argument_is_undefined?(node, n) } ||
83
97
  pattern_matching_nodes(condition).any?
98
+
84
99
  return unless (msg = message(node))
85
100
 
86
101
  add_offense(node.loc.keyword, message: format(msg, keyword: node.keyword)) do |corrector|
@@ -90,9 +105,14 @@ module RuboCop
90
105
  ignore_node(node)
91
106
  end
92
107
  end
108
+ # rubocop:enable Metrics/AbcSize
93
109
 
94
110
  private
95
111
 
112
+ def endless_method?(body)
113
+ body&.any_def_type? && body.endless?
114
+ end
115
+
96
116
  def defined_nodes(condition)
97
117
  if condition.defined_type?
98
118
  [condition]
@@ -112,12 +132,10 @@ module RuboCop
112
132
  end
113
133
 
114
134
  def pattern_matching_nodes(condition)
115
- if condition.type?(:match_pattern, :match_pattern_p)
135
+ if condition.any_match_pattern_type?
116
136
  [condition]
117
137
  else
118
- condition.each_descendant.select do |node|
119
- node.type?(:match_pattern, :match_pattern_p)
120
- end
138
+ condition.each_descendant.select(&:any_match_pattern_type?)
121
139
  end
122
140
  end
123
141
 
@@ -28,19 +28,16 @@ module RuboCop
28
28
 
29
29
  MSG = 'Avoid modifier `%<keyword>s` after another conditional.'
30
30
 
31
+ # rubocop:disable Metrics/AbcSize
31
32
  def on_if(node)
32
33
  return unless node.modifier_form? && node.body.if_type?
33
34
 
34
35
  add_offense(node.loc.keyword, message: format(MSG, keyword: node.keyword)) do |corrector|
35
- keyword = node.if? ? 'if' : 'unless'
36
-
37
- corrector.replace(node, <<~RUBY.chop)
38
- #{keyword} #{node.condition.source}
39
- #{node.if_branch.source}
40
- end
41
- RUBY
36
+ corrector.wrap(node.if_branch, "#{node.keyword} #{node.condition.source}\n", "\nend")
37
+ corrector.remove(node.if_branch.source_range.end.join(node.condition.source_range.end))
42
38
  end
43
39
  end
40
+ # rubocop:enable Metrics/AbcSize
44
41
  end
45
42
  end
46
43
  end
@@ -10,7 +10,7 @@ module RuboCop
10
10
  # `nonzero?` method is allowed by default.
11
11
  # These are customizable with `AllowedMethods` option.
12
12
  #
13
- # This cop targets only `if`s with a single `elsif` or `else` branch. The following
13
+ # This cop targets only ``if``s with a single `elsif` or `else` branch. The following
14
14
  # code will be allowed, because it has two `elsif` branches:
15
15
  #
16
16
  # [source,ruby]
@@ -5,15 +5,28 @@ module RuboCop
5
5
  module Style
6
6
  # Checks for blocks with one argument where `it` block parameter can be used.
7
7
  #
8
- # It provides three `EnforcedStyle` options:
8
+ # It provides four `EnforcedStyle` options:
9
9
  #
10
- # 1. `only_numbered_parameters` (default) ... Detects only numbered block parameters.
11
- # 2. `always` ... Always uses the `it` block parameter.
12
- # 3. `disallow` ... Disallows the `it` block parameter.
10
+ # 1. `allow_single_line` (default) ... Always uses the `it` block parameter in a single line.
11
+ # 2. `only_numbered_parameters` ... Detects only numbered block parameters.
12
+ # 3. `always` ... Always uses the `it` block parameter.
13
+ # 4. `disallow` ... Disallows the `it` block parameter.
13
14
  #
14
- # A single numbered parameter is detected when `only_numbered_parameters` or `always`.
15
+ # A single numbered parameter is detected when `allow_single_line`,
16
+ # `only_numbered_parameters`, or `always`.
15
17
  #
16
- # @example EnforcedStyle: only_numbered_parameters (default)
18
+ # @example EnforcedStyle: allow_single_line (default)
19
+ # # bad
20
+ # block do
21
+ # do_something(it)
22
+ # end
23
+ # block { do_something(_1) }
24
+ #
25
+ # # good
26
+ # block { do_something(it) }
27
+ # block { |named_param| do_something(named_param) }
28
+ #
29
+ # @example EnforcedStyle: only_numbered_parameters
17
30
  # # bad
18
31
  # block { do_something(_1) }
19
32
  #
@@ -42,8 +55,9 @@ module RuboCop
42
55
  extend TargetRubyVersion
43
56
  extend AutoCorrector
44
57
 
45
- MSG_USE_IT_BLOCK_PARAMETER = 'Use `it` block parameter.'
46
- MSG_AVOID_IT_BLOCK_PARAMETER = 'Avoid using `it` block parameter.'
58
+ MSG_USE_IT_PARAMETER = 'Use `it` block parameter.'
59
+ MSG_AVOID_IT_PARAMETER = 'Avoid using `it` block parameter.'
60
+ MSG_AVOID_IT_PARAMETER_MULTI_LINE = 'Avoid using numbered parameters for multi-line blocks.'
47
61
 
48
62
  minimum_target_ruby_version 3.4
49
63
 
@@ -57,7 +71,7 @@ module RuboCop
57
71
  variables = find_block_variables(node, node.first_argument.source)
58
72
 
59
73
  variables.each do |variable|
60
- add_offense(variable, message: MSG_USE_IT_BLOCK_PARAMETER) do |corrector|
74
+ add_offense(variable, message: MSG_USE_IT_PARAMETER) do |corrector|
61
75
  corrector.remove(node.arguments)
62
76
  corrector.replace(variable, 'it')
63
77
  end
@@ -71,19 +85,24 @@ module RuboCop
71
85
  variables = find_block_variables(node, '_1')
72
86
 
73
87
  variables.each do |variable|
74
- add_offense(variable, message: MSG_USE_IT_BLOCK_PARAMETER) do |corrector|
88
+ add_offense(variable, message: MSG_USE_IT_PARAMETER) do |corrector|
75
89
  corrector.replace(variable, 'it')
76
90
  end
77
91
  end
78
92
  end
79
93
 
80
94
  def on_itblock(node)
81
- return unless style == :disallow
95
+ case style
96
+ when :allow_single_line
97
+ return if node.single_line?
82
98
 
83
- variables = find_block_variables(node, 'it')
99
+ add_offense(node, message: MSG_AVOID_IT_PARAMETER_MULTI_LINE)
100
+ when :disallow
101
+ variables = find_block_variables(node, 'it')
84
102
 
85
- variables.each do |variable|
86
- add_offense(variable, message: MSG_AVOID_IT_BLOCK_PARAMETER)
103
+ variables.each do |variable|
104
+ add_offense(variable, message: MSG_AVOID_IT_PARAMETER)
105
+ end
87
106
  end
88
107
  end
89
108
 
@@ -42,7 +42,7 @@ module RuboCop
42
42
  return if kwarg_nodes.empty?
43
43
 
44
44
  add_offense(node) do |corrector|
45
- defining_node = node.each_ancestor(:def, :defs, :block).first
45
+ defining_node = node.each_ancestor(:any_def, :block).first
46
46
  next if processed_source.contains_comment?(arguments_range(defining_node))
47
47
  next unless node.parent.find(&:kwoptarg_type?) == node
48
48
 
@@ -54,9 +54,14 @@ module RuboCop
54
54
 
55
55
  def prefer(node)
56
56
  receiver = node.receiver.source
57
- arguments = node.arguments.map(&:source).join(', ')
58
57
  dot = node.loc.dot.source
59
- method = explicit_style? ? "call(#{arguments})" : "(#{arguments})"
58
+ call_arguments = if node.arguments.empty?
59
+ ''
60
+ else
61
+ arguments = node.arguments.map(&:source).join(', ')
62
+ "(#{arguments})"
63
+ end
64
+ method = explicit_style? ? "call#{call_arguments}" : "(#{arguments})"
60
65
 
61
66
  "#{receiver}#{dot}#{method}"
62
67
  end
@@ -212,9 +212,11 @@ module RuboCop
212
212
  end
213
213
 
214
214
  def correct_push_node(corrector, push_node)
215
+ arg_node = push_node.first_argument
215
216
  range = push_node.source_range
216
- arg_range = push_node.first_argument.source_range
217
+ arg_range = arg_node.source_range
217
218
 
219
+ corrector.wrap(arg_node, '{ ', ' }') if arg_node.hash_type? && !arg_node.braces?
218
220
  corrector.remove(range_between(range.begin_pos, arg_range.begin_pos))
219
221
  corrector.remove(range_between(arg_range.end_pos, range.end_pos))
220
222
  end
@@ -45,6 +45,11 @@ module RuboCop
45
45
  }
46
46
  PATTERN
47
47
 
48
+ # @!method destructuring_argument(node)
49
+ def_node_matcher :destructuring_argument, <<~PATTERN
50
+ (args $(mlhs (arg _)+))
51
+ PATTERN
52
+
48
53
  def self.autocorrect_incompatible_with
49
54
  [Layout::SingleLineBlockChain]
50
55
  end
@@ -73,6 +78,12 @@ module RuboCop
73
78
  corrector.replace(map_dot, to_h.loc.dot.source)
74
79
  end
75
80
  corrector.replace(map.loc.selector, 'to_h')
81
+
82
+ return unless map.parent.block_type?
83
+
84
+ if (argument = destructuring_argument(map.parent.arguments))
85
+ corrector.replace(argument, argument.source[1..-2])
86
+ end
76
87
  end
77
88
  # rubocop:enable Metrics/AbcSize
78
89
  end
@@ -49,7 +49,7 @@ module RuboCop
49
49
 
50
50
  def inside_endless_method_def?(node)
51
51
  # parens are required around arguments inside an endless method
52
- node.each_ancestor(:def, :defs).any?(&:endless?) && node.arguments.any?
52
+ node.each_ancestor(:any_def).any?(&:endless?) && node.arguments.any?
53
53
  end
54
54
 
55
55
  def require_parentheses_for_hash_value_omission?(node) # rubocop:disable Metrics/PerceivedComplexity
@@ -167,7 +167,7 @@ module RuboCop
167
167
  def call_in_match_pattern?(node)
168
168
  return false unless (parent = node.parent)
169
169
 
170
- parent.type?(:match_pattern, :match_pattern_p)
170
+ parent.any_match_pattern_type?
171
171
  end
172
172
 
173
173
  def hash_literal_in_arguments?(node)
@@ -222,6 +222,9 @@ module RuboCop
222
222
  end
223
223
 
224
224
  def unary_literal?(node)
225
+ # NOTE: should be removed after releasing https://github.com/rubocop/rubocop-ast/pull/379
226
+ return node.source.match?(/\A[+-]/) if node.complex_type?
227
+
225
228
  (node.numeric_type? && node.sign?) ||
226
229
  (node.parent&.send_type? && node.parent.unary_operation?)
227
230
  end
@@ -23,11 +23,13 @@ module RuboCop
23
23
  'clause in a multiline statement.'
24
24
 
25
25
  def on_if(node)
26
+ return if part_of_ignored_node?(node)
26
27
  return unless node.modifier_form? && node.body.multiline?
27
28
 
28
29
  add_offense(node, message: format(MSG, keyword: node.keyword)) do |corrector|
29
30
  corrector.replace(node, to_normal_if(node))
30
31
  end
32
+ ignore_node(node)
31
33
  end
32
34
 
33
35
  private
@@ -45,7 +45,7 @@ module RuboCop
45
45
  # Report offense only if changing case doesn't change semantics,
46
46
  # i.e., if the string would become dynamic or has special characters.
47
47
  ast = parse(corrected(node.source)).ast
48
- return if node.children != ast.children
48
+ return if node.children != ast&.children
49
49
 
50
50
  add_offense(node.loc.begin) do |corrector|
51
51
  corrector.replace(node, corrected(node.source))
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for redundant calls of `Array#flatten`.
7
+ #
8
+ # `Array#join` joins nested arrays recursively, so flattening an array
9
+ # beforehand is redundant.
10
+ #
11
+ # @safety
12
+ # Cop is unsafe because the receiver of `flatten` method might not
13
+ # be an `Array`, so it's possible it won't respond to `join` method,
14
+ # or the end result would be different.
15
+ #
16
+ # @example
17
+ # # bad
18
+ # x.flatten.join
19
+ # x.flatten(1).join
20
+ #
21
+ # # good
22
+ # x.join
23
+ #
24
+ class RedundantArrayFlatten < Base
25
+ extend AutoCorrector
26
+
27
+ MSG = 'Remove the redundant `flatten`.'
28
+
29
+ RESTRICT_ON_SEND = %i[flatten].freeze
30
+
31
+ # @!method flatten_join?(node)
32
+ def_node_matcher :flatten_join?, <<~PATTERN
33
+ (call (call !nil? :flatten _?) :join _?)
34
+ PATTERN
35
+
36
+ def on_send(node)
37
+ return unless flatten_join?(node.parent)
38
+
39
+ range = node.loc.dot.begin.join(node.source_range.end)
40
+ add_offense(range) do |corrector|
41
+ corrector.remove(range)
42
+ end
43
+ end
44
+ alias on_csend on_send
45
+ end
46
+ end
47
+ end
48
+ end
@@ -230,6 +230,14 @@ module RuboCop
230
230
  node.type?(:kwsplat, :forwarded_kwrestarg)
231
231
  end
232
232
 
233
+ def wrap_arguments_with_parens(condition)
234
+ method = condition.source_range.begin.join(condition.loc.selector.end)
235
+ arguments = condition.first_argument.source_range.begin.join(condition.source_range.end)
236
+
237
+ "#{method.source}(#{arguments.source})"
238
+ end
239
+
240
+ # rubocop:disable Metrics/AbcSize
233
241
  def if_source(if_branch, arithmetic_operation)
234
242
  if branches_have_method?(if_branch.parent) && if_branch.parenthesized?
235
243
  if_branch.source.delete_suffix(')')
@@ -238,11 +246,15 @@ module RuboCop
238
246
 
239
247
  "#{if_branch.receiver.source} #{if_branch.method_name} (#{argument_source}"
240
248
  elsif if_branch.true_type?
241
- if_branch.parent.condition.source
249
+ condition = if_branch.parent.condition
250
+ return condition.source if condition.arguments.empty?
251
+
252
+ wrap_arguments_with_parens(condition)
242
253
  else
243
254
  if_branch.source
244
255
  end
245
256
  end
257
+ # rubocop:enable Metrics/AbcSize
246
258
 
247
259
  def else_source(else_branch, arithmetic_operation) # rubocop:disable Metrics/AbcSize
248
260
  if arithmetic_operation
@@ -20,7 +20,7 @@ module RuboCop
20
20
 
21
21
  MSG = 'Remove the redundant current directory path.'
22
22
  RESTRICT_ON_SEND = %i[require_relative].freeze
23
- CURRENT_DIRECTORY_PREFIX = %r{./+}.freeze
23
+ CURRENT_DIRECTORY_PREFIX = %r{\./+}.freeze
24
24
  REDUNDANT_CURRENT_DIRECTORY_PREFIX = /\A#{CURRENT_DIRECTORY_PREFIX}/.freeze
25
25
 
26
26
  def on_send(node)
@@ -121,7 +121,12 @@ module RuboCop
121
121
  def register_all_fields_literal(node, string, arguments)
122
122
  return unless all_fields_literal?(string, arguments.dup)
123
123
 
124
- formatted_string = format(string, *argument_values(arguments))
124
+ format_arguments = argument_values(arguments)
125
+ begin
126
+ formatted_string = format(string, *format_arguments)
127
+ rescue ArgumentError
128
+ return
129
+ end
125
130
  replacement = quote(formatted_string, node)
126
131
 
127
132
  add_offense(node, message: message(node, replacement)) do |corrector|