rubocop 1.75.2 → 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 (97) 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 +1 -0
  12. data/lib/rubocop/cop/internal_affairs/undefined_config.rb +6 -1
  13. data/lib/rubocop/cop/layout/class_structure.rb +35 -0
  14. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
  15. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +6 -2
  16. data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -1
  17. data/lib/rubocop/cop/layout/hash_alignment.rb +2 -2
  18. data/lib/rubocop/cop/layout/leading_comment_space.rb +13 -1
  19. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +1 -1
  20. data/lib/rubocop/cop/layout/space_after_semicolon.rb +10 -0
  21. data/lib/rubocop/cop/layout/space_before_brackets.rb +6 -32
  22. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +5 -1
  23. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +3 -0
  24. data/lib/rubocop/cop/lint/array_literal_in_regexp.rb +2 -3
  25. data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -1
  26. data/lib/rubocop/cop/lint/circular_argument_reference.rb +2 -5
  27. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +1 -1
  28. data/lib/rubocop/cop/lint/duplicate_methods.rb +84 -2
  29. data/lib/rubocop/cop/lint/empty_interpolation.rb +3 -1
  30. data/lib/rubocop/cop/lint/float_comparison.rb +27 -0
  31. data/lib/rubocop/cop/lint/identity_comparison.rb +19 -15
  32. data/lib/rubocop/cop/lint/literal_as_condition.rb +31 -25
  33. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +1 -1
  34. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +5 -0
  35. data/lib/rubocop/cop/lint/useless_assignment.rb +2 -0
  36. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +87 -0
  37. data/lib/rubocop/cop/lint/useless_or.rb +98 -0
  38. data/lib/rubocop/cop/lint/useless_rescue.rb +1 -1
  39. data/lib/rubocop/cop/lint/void.rb +2 -2
  40. data/lib/rubocop/cop/message_annotator.rb +7 -3
  41. data/lib/rubocop/cop/metrics/abc_size.rb +1 -1
  42. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +15 -14
  43. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +2 -0
  44. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
  45. data/lib/rubocop/cop/mixin/trailing_comma.rb +9 -5
  46. data/lib/rubocop/cop/naming/predicate_method.rb +216 -0
  47. data/lib/rubocop/cop/naming/{predicate_name.rb → predicate_prefix.rb} +2 -2
  48. data/lib/rubocop/cop/style/access_modifier_declarations.rb +32 -10
  49. data/lib/rubocop/cop/style/arguments_forwarding.rb +8 -5
  50. data/lib/rubocop/cop/style/class_and_module_children.rb +19 -3
  51. data/lib/rubocop/cop/style/command_literal.rb +1 -1
  52. data/lib/rubocop/cop/style/commented_keyword.rb +2 -2
  53. data/lib/rubocop/cop/style/comparable_between.rb +5 -2
  54. data/lib/rubocop/cop/style/conditional_assignment.rb +15 -6
  55. data/lib/rubocop/cop/style/data_inheritance.rb +7 -0
  56. data/lib/rubocop/cop/style/def_with_parentheses.rb +18 -5
  57. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +100 -0
  58. data/lib/rubocop/cop/style/eval_with_location.rb +3 -3
  59. data/lib/rubocop/cop/style/hash_fetch_chain.rb +0 -1
  60. data/lib/rubocop/cop/style/hash_transform_keys.rb +2 -2
  61. data/lib/rubocop/cop/style/hash_transform_values.rb +2 -2
  62. data/lib/rubocop/cop/style/identical_conditional_branches.rb +3 -3
  63. data/lib/rubocop/cop/style/if_unless_modifier.rb +22 -4
  64. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +4 -7
  65. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +1 -1
  66. data/lib/rubocop/cop/style/it_block_parameter.rb +33 -14
  67. data/lib/rubocop/cop/style/lambda_call.rb +7 -2
  68. data/lib/rubocop/cop/style/map_into_array.rb +3 -1
  69. data/lib/rubocop/cop/style/map_to_hash.rb +11 -0
  70. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +4 -1
  71. data/lib/rubocop/cop/style/multiline_if_modifier.rb +2 -0
  72. data/lib/rubocop/cop/style/percent_q_literals.rb +1 -1
  73. data/lib/rubocop/cop/style/redundant_array_flatten.rb +48 -0
  74. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +1 -1
  75. data/lib/rubocop/cop/style/redundant_format.rb +6 -1
  76. data/lib/rubocop/cop/style/redundant_line_continuation.rb +0 -3
  77. data/lib/rubocop/cop/style/redundant_parentheses.rb +31 -3
  78. data/lib/rubocop/cop/style/regexp_literal.rb +1 -1
  79. data/lib/rubocop/cop/style/safe_navigation.rb +28 -10
  80. data/lib/rubocop/cop/style/sole_nested_conditional.rb +4 -2
  81. data/lib/rubocop/cop/style/string_concatenation.rb +1 -2
  82. data/lib/rubocop/cop/style/struct_inheritance.rb +8 -1
  83. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +7 -1
  84. data/lib/rubocop/cop/style/trailing_comma_in_array_literal.rb +1 -1
  85. data/lib/rubocop/cop/style/trailing_comma_in_hash_literal.rb +1 -1
  86. data/lib/rubocop/cop/team.rb +1 -1
  87. data/lib/rubocop/cop/variable_force/assignment.rb +7 -3
  88. data/lib/rubocop/cops_documentation_generator.rb +6 -2
  89. data/lib/rubocop/formatter/disabled_config_formatter.rb +2 -1
  90. data/lib/rubocop/formatter/html_formatter.rb +1 -1
  91. data/lib/rubocop/formatter/pacman_formatter.rb +1 -1
  92. data/lib/rubocop/rspec/cop_helper.rb +2 -2
  93. data/lib/rubocop/rspec/shared_contexts.rb +1 -2
  94. data/lib/rubocop/target_finder.rb +5 -1
  95. data/lib/rubocop/version.rb +1 -1
  96. data/lib/rubocop.rb +6 -1
  97. metadata +12 -7
@@ -16,16 +16,16 @@ module RuboCop
16
16
  #
17
17
  # In Ruby 3.2, anonymous args/kwargs forwarding has been added.
18
18
  #
19
- # This cop also identifies places where `use_args(*args)`/`use_kwargs(**kwargs)` can be
20
- # replaced by `use_args(*)`/`use_kwargs(**)`; if desired, this functionality can be disabled
21
- # by setting `UseAnonymousForwarding: false`.
19
+ # This cop also identifies places where `+use_args(*args)+`/`+use_kwargs(**kwargs)+` can be
20
+ # replaced by `+use_args(*)+`/`+use_kwargs(**)+`; if desired, this functionality can be
21
+ # disabled by setting `UseAnonymousForwarding: false`.
22
22
  #
23
23
  # And this cop has `RedundantRestArgumentNames`, `RedundantKeywordRestArgumentNames`,
24
24
  # and `RedundantBlockArgumentNames` options. This configuration is a list of redundant names
25
25
  # that are sufficient for anonymizing meaningless naming.
26
26
  #
27
27
  # Meaningless names that are commonly used can be anonymized by default:
28
- # e.g., `*args`, `**options`, `&block`, and so on.
28
+ # e.g., `+*args+`, `+**options+`, `&block`, and so on.
29
29
  #
30
30
  # Names not on this list are likely to be meaningful and are allowed by default.
31
31
  #
@@ -146,7 +146,7 @@ module RuboCop
146
146
  minimum_target_ruby_version 2.7
147
147
 
148
148
  FORWARDING_LVAR_TYPES = %i[splat kwsplat block_pass].freeze
149
- ADDITIONAL_ARG_TYPES = %i[lvar arg].freeze
149
+ ADDITIONAL_ARG_TYPES = %i[lvar arg optarg].freeze
150
150
 
151
151
  FORWARDING_MSG = 'Use shorthand syntax `...` for arguments forwarding.'
152
152
  ARGS_MSG = 'Use anonymous positional arguments forwarding (`*`).'
@@ -479,6 +479,9 @@ module RuboCop
479
479
  end
480
480
 
481
481
  def ruby_32_only_anonymous_forwarding?
482
+ # A block argument and an anonymous block argument are never passed together.
483
+ return false if @send_node.each_ancestor(:any_block).any?
484
+
482
485
  def_all_anonymous_args?(@def_node) && send_all_anonymous_args?(@send_node)
483
486
  end
484
487
 
@@ -131,21 +131,27 @@ module RuboCop
131
131
  "#{node.body.children.first.const_name}"
132
132
  end
133
133
 
134
+ # rubocop:disable Metrics/AbcSize
134
135
  def remove_end(corrector, body)
135
- remove_begin_pos = body.loc.end.begin_pos - leading_spaces(body).size
136
+ remove_begin_pos = if same_line?(body.loc.name, body.loc.end)
137
+ body.loc.name.end_pos
138
+ else
139
+ body.loc.end.begin_pos - leading_spaces(body).size
140
+ end
136
141
  adjustment = processed_source.raw_source[remove_begin_pos] == ';' ? 0 : 1
137
142
  range = range_between(remove_begin_pos, body.loc.end.end_pos + adjustment)
138
143
 
139
144
  corrector.remove(range)
140
145
  end
146
+ # rubocop:enable Metrics/AbcSize
141
147
 
142
148
  def unindent(corrector, node)
143
149
  return unless node.body.children.last
144
150
 
145
151
  last_child_leading_spaces = leading_spaces(node.body.children.last)
146
- return if leading_spaces(node).size == last_child_leading_spaces.size
152
+ return if spaces_size(leading_spaces(node)) == spaces_size(last_child_leading_spaces)
147
153
 
148
- column_delta = configured_indentation_width - last_child_leading_spaces.size
154
+ column_delta = configured_indentation_width - spaces_size(last_child_leading_spaces)
149
155
  return if column_delta.zero?
150
156
 
151
157
  AlignmentCorrector.correct(corrector, processed_source, node, column_delta)
@@ -155,6 +161,16 @@ module RuboCop
155
161
  node.source_range.source_line[/\A\s*/]
156
162
  end
157
163
 
164
+ def spaces_size(spaces_string)
165
+ mapping = { "\t" => tab_indentation_width }
166
+ spaces_string.chars.sum { |character| mapping.fetch(character, 1) }
167
+ end
168
+
169
+ def tab_indentation_width
170
+ config.for_cop('Layout/IndentationStyle')['IndentationWidth'] ||
171
+ configured_indentation_width
172
+ end
173
+
158
174
  def check_style(node, body, style)
159
175
  return if node.identifier.namespace&.cbase_type?
160
176
 
@@ -173,7 +173,7 @@ module RuboCop
173
173
  end
174
174
 
175
175
  def preferred_delimiters_config
176
- config.for_cop('Style/PercentLiteralDelimiters') ['PreferredDelimiters']
176
+ config.for_cop('Style/PercentLiteralDelimiters')['PreferredDelimiters']
177
177
  end
178
178
  end
179
179
  end
@@ -58,7 +58,7 @@ module RuboCop
58
58
  REGEXP = /(?<keyword>\S+).*#/.freeze
59
59
 
60
60
  SUBCLASS_DEFINITION = /\A\s*class\s+(\w|::)+\s*<\s*(\w|::)+/.freeze
61
- METHOD_DEFINITION = /\A\s*def\s/.freeze
61
+ METHOD_OR_END_DEFINITIONS = /\A\s*(def\s|end)/.freeze
62
62
 
63
63
  STEEP_REGEXP = /#\ssteep:ignore(\s|\z)/.freeze
64
64
 
@@ -102,7 +102,7 @@ module RuboCop
102
102
  case line
103
103
  when SUBCLASS_DEFINITION
104
104
  comment.text.start_with?(/#\[.+\]/)
105
- when METHOD_DEFINITION
105
+ when METHOD_OR_END_DEFINITIONS
106
106
  comment.text.start_with?('#:')
107
107
  else
108
108
  false
@@ -9,6 +9,9 @@ module RuboCop
9
9
  # although the difference generally isn't observable. If you require maximum
10
10
  # performance, consider using logical comparison.
11
11
  #
12
+ # @safety
13
+ # This cop is unsafe because the receiver may not respond to `between?`.
14
+ #
12
15
  # @example
13
16
  #
14
17
  # # bad
@@ -61,8 +64,8 @@ module RuboCop
61
64
 
62
65
  def register_offense(node, min_and_value, max_and_value)
63
66
  value = (min_and_value & max_and_value).first
64
- min = min_and_value.find { _1 != value }
65
- max = max_and_value.find { _1 != value }
67
+ min = min_and_value.find { _1 != value } || value
68
+ max = max_and_value.find { _1 != value } || value
66
69
 
67
70
  prefer = "#{value.source}.between?(#{min.source}, #{max.source})"
68
71
  add_offense(node, message: format(MSG, prefer: prefer)) do |corrector|
@@ -314,6 +314,7 @@ module RuboCop
314
314
 
315
315
  def assignment_node(node)
316
316
  assignment = node.send_type? ? node.last_argument : node.expression
317
+ return unless assignment
317
318
 
318
319
  # ignore pseudo-assignments without rhs in for nodes
319
320
  return if node.parent&.for_type?
@@ -436,7 +437,7 @@ module RuboCop
436
437
  # Helper module to provide common methods to ConditionalAssignment
437
438
  # correctors
438
439
  module ConditionalCorrectorHelper
439
- # rubocop:disable Metrics/AbcSize
440
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
440
441
  def remove_whitespace_in_branches(corrector, branch, condition, column)
441
442
  branch.each_node do |child|
442
443
  next if child.source_range.nil?
@@ -446,13 +447,19 @@ module RuboCop
446
447
  corrector.remove(white_space) if white_space.source.strip.empty?
447
448
  end
448
449
 
449
- [condition.loc.else, condition.loc.end].each do |loc|
450
- next unless loc
451
-
452
- corrector.remove_preceding(loc, loc.column - column)
450
+ if condition.loc.else && !same_line?(condition.else_branch, condition)
451
+ corrector.remove_preceding(condition.loc.else, condition.loc.else.column - column)
453
452
  end
453
+
454
+ return unless condition.loc.end && !same_line?(condition.loc.end, condition)
455
+
456
+ corrector.remove_preceding(condition.loc.end, condition.loc.end.column - column)
457
+ end
458
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
459
+
460
+ def same_line?(node1, node2)
461
+ RuboCop::Cop::Util.same_line?(node1, node2)
454
462
  end
455
- # rubocop:enable Metrics/AbcSize
456
463
 
457
464
  def white_space_range(node, column)
458
465
  expression = node.source_range
@@ -598,6 +605,8 @@ module RuboCop
598
605
 
599
606
  return unless (branch_else = branch.parent.loc.else)
600
607
 
608
+ return if same_line?(branch_else, condition)
609
+
601
610
  corrector.remove_preceding(branch_else, branch_else.column - column)
602
611
  end
603
612
  end
@@ -4,6 +4,7 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # Checks for inheritance from `Data.define` to avoid creating the anonymous parent class.
7
+ # Inheriting from `Data.define` adds a superfluous level in inheritance tree.
7
8
  #
8
9
  # @safety
9
10
  # Autocorrection is unsafe because it will change the inheritance
@@ -17,12 +18,18 @@ module RuboCop
17
18
  # end
18
19
  # end
19
20
  #
21
+ # Person.ancestors
22
+ # # => [Person, #<Class:0x000000010b4e14a0>, Data, (...)]
23
+ #
20
24
  # # good
21
25
  # Person = Data.define(:first_name, :last_name) do
22
26
  # def age
23
27
  # 42
24
28
  # end
25
29
  # end
30
+ #
31
+ # Person.ancestors
32
+ # # => [Person, Data, (...)]
26
33
  class DataInheritance < Base
27
34
  include RangeHelp
28
35
  extend AutoCorrector
@@ -25,8 +25,9 @@ module RuboCop
25
25
  # # good
26
26
  # def foo = do_something
27
27
  #
28
- # # good (without parentheses it's a syntax error)
28
+ # # good - without parentheses it's a syntax error
29
29
  # def foo() do_something end
30
+ # def foo()=do_something
30
31
  #
31
32
  # # bad
32
33
  # def Baz.foo()
@@ -38,19 +39,31 @@ module RuboCop
38
39
  # do_something
39
40
  # end
40
41
  class DefWithParentheses < Base
42
+ include RangeHelp
41
43
  extend AutoCorrector
42
44
 
43
45
  MSG = "Omit the parentheses in defs when the method doesn't accept any arguments."
44
46
 
45
47
  def on_def(node)
46
- return if node.single_line? && !node.endless?
47
- return unless !node.arguments? && (node_arguments = node.arguments.source_range)
48
+ return unless !node.arguments? && (arguments_range = node.arguments.source_range)
49
+ return if parentheses_required?(node, arguments_range)
48
50
 
49
- add_offense(node_arguments) do |corrector|
50
- corrector.remove(node_arguments)
51
+ add_offense(arguments_range) do |corrector|
52
+ corrector.remove(arguments_range)
51
53
  end
52
54
  end
53
55
  alias on_defs on_def
56
+
57
+ private
58
+
59
+ def parentheses_required?(node, arguments_range)
60
+ return true if node.single_line? && !node.endless?
61
+
62
+ end_pos = arguments_range.end.end_pos
63
+ token_after_argument = range_between(end_pos, end_pos + 1).source
64
+
65
+ token_after_argument == '='
66
+ end
54
67
  end
55
68
  end
56
69
  end
@@ -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
@@ -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
@@ -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
 
@@ -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