rubocop 1.42.0 → 1.45.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +73 -31
  4. data/lib/rubocop/cli.rb +54 -8
  5. data/lib/rubocop/config_loader.rb +12 -15
  6. data/lib/rubocop/config_loader_resolver.rb +3 -4
  7. data/lib/rubocop/cop/base.rb +27 -9
  8. data/lib/rubocop/cop/commissioner.rb +8 -2
  9. data/lib/rubocop/cop/cop.rb +23 -3
  10. data/lib/rubocop/cop/corrector.rb +10 -2
  11. data/lib/rubocop/cop/correctors/ordered_gem_corrector.rb +1 -6
  12. data/lib/rubocop/cop/gemspec/development_dependencies.rb +107 -0
  13. data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +11 -3
  14. data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
  15. data/lib/rubocop/cop/layout/block_end_newline.rb +7 -1
  16. data/lib/rubocop/cop/layout/class_structure.rb +2 -16
  17. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +2 -6
  18. data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -1
  19. data/lib/rubocop/cop/layout/heredoc_indentation.rb +6 -9
  20. data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
  21. data/lib/rubocop/cop/layout/space_around_operators.rb +1 -1
  22. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +11 -13
  23. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +4 -4
  24. data/lib/rubocop/cop/layout/space_inside_string_interpolation.rb +5 -4
  25. data/lib/rubocop/cop/lint/ambiguous_operator.rb +4 -0
  26. data/lib/rubocop/cop/lint/debugger.rb +8 -27
  27. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +62 -112
  28. data/lib/rubocop/cop/lint/else_layout.rb +2 -6
  29. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +14 -7
  30. data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +15 -17
  31. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +1 -1
  32. data/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb +1 -0
  33. data/lib/rubocop/cop/lint/nested_method_definition.rb +8 -5
  34. data/lib/rubocop/cop/lint/redundant_require_statement.rb +11 -1
  35. data/lib/rubocop/cop/lint/useless_access_modifier.rb +7 -4
  36. data/lib/rubocop/cop/lint/useless_method_definition.rb +3 -3
  37. data/lib/rubocop/cop/lint/useless_rescue.rb +85 -0
  38. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +9 -1
  39. data/lib/rubocop/cop/lint/void.rb +19 -10
  40. data/lib/rubocop/cop/metrics/block_length.rb +1 -1
  41. data/lib/rubocop/cop/metrics/block_nesting.rb +1 -1
  42. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +1 -1
  43. data/lib/rubocop/cop/metrics/parameter_lists.rb +27 -0
  44. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +2 -5
  45. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  46. data/lib/rubocop/cop/mixin/allowed_methods.rb +3 -1
  47. data/lib/rubocop/cop/mixin/comments_help.rb +5 -3
  48. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +57 -23
  49. data/lib/rubocop/cop/mixin/line_length_help.rb +3 -1
  50. data/lib/rubocop/cop/mixin/statement_modifier.rb +1 -0
  51. data/lib/rubocop/cop/mixin/surrounding_space.rb +3 -3
  52. data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
  53. data/lib/rubocop/cop/naming/block_forwarding.rb +4 -0
  54. data/lib/rubocop/cop/naming/class_and_module_camel_case.rb +1 -1
  55. data/lib/rubocop/cop/registry.rb +12 -7
  56. data/lib/rubocop/cop/style/access_modifier_declarations.rb +26 -11
  57. data/lib/rubocop/cop/style/arguments_forwarding.rb +1 -0
  58. data/lib/rubocop/cop/style/block_delimiters.rb +8 -2
  59. data/lib/rubocop/cop/style/class_and_module_children.rb +3 -10
  60. data/lib/rubocop/cop/style/command_literal.rb +1 -1
  61. data/lib/rubocop/cop/style/comparable_clamp.rb +125 -0
  62. data/lib/rubocop/cop/style/conditional_assignment.rb +0 -6
  63. data/lib/rubocop/cop/style/documentation.rb +1 -1
  64. data/lib/rubocop/cop/style/documentation_method.rb +6 -0
  65. data/lib/rubocop/cop/style/hash_each_methods.rb +13 -1
  66. data/lib/rubocop/cop/style/hash_syntax.rb +1 -0
  67. data/lib/rubocop/cop/style/infinite_loop.rb +2 -5
  68. data/lib/rubocop/cop/style/invertible_unless_condition.rb +114 -0
  69. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +23 -14
  70. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +2 -0
  71. data/lib/rubocop/cop/style/min_max_comparison.rb +11 -1
  72. data/lib/rubocop/cop/style/missing_else.rb +13 -1
  73. data/lib/rubocop/cop/style/multiline_if_modifier.rb +0 -4
  74. data/lib/rubocop/cop/style/multiline_memoization.rb +2 -2
  75. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +18 -3
  76. data/lib/rubocop/cop/style/negated_if_else_condition.rb +1 -5
  77. data/lib/rubocop/cop/style/numbered_parameters_limit.rb +11 -3
  78. data/lib/rubocop/cop/style/one_line_conditional.rb +3 -6
  79. data/lib/rubocop/cop/style/operator_method_call.rb +16 -2
  80. data/lib/rubocop/cop/style/parallel_assignment.rb +3 -1
  81. data/lib/rubocop/cop/style/redundant_condition.rb +16 -1
  82. data/lib/rubocop/cop/style/redundant_conditional.rb +0 -4
  83. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +16 -10
  84. data/lib/rubocop/cop/style/redundant_heredoc_delimiter_quotes.rb +58 -0
  85. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +2 -1
  86. data/lib/rubocop/cop/style/require_order.rb +2 -9
  87. data/lib/rubocop/cop/style/self_assignment.rb +2 -2
  88. data/lib/rubocop/cop/style/semicolon.rb +24 -2
  89. data/lib/rubocop/cop/style/string_hash_keys.rb +4 -1
  90. data/lib/rubocop/cop/style/symbol_array.rb +1 -1
  91. data/lib/rubocop/cop/style/word_array.rb +1 -1
  92. data/lib/rubocop/cop/style/yoda_condition.rb +12 -5
  93. data/lib/rubocop/cop/style/yoda_expression.rb +18 -2
  94. data/lib/rubocop/cop/team.rb +19 -14
  95. data/lib/rubocop/cop/variable_force/scope.rb +3 -3
  96. data/lib/rubocop/cop/variable_force/variable_table.rb +3 -1
  97. data/lib/rubocop/cop/variable_force.rb +1 -1
  98. data/lib/rubocop/formatter.rb +0 -1
  99. data/lib/rubocop/options.rb +22 -1
  100. data/lib/rubocop/path_util.rb +11 -6
  101. data/lib/rubocop/rspec/expect_offense.rb +6 -4
  102. data/lib/rubocop/runner.rb +40 -4
  103. data/lib/rubocop/server/cache.rb +10 -3
  104. data/lib/rubocop/server/cli.rb +37 -18
  105. data/lib/rubocop/server/client_command/exec.rb +1 -1
  106. data/lib/rubocop/server/client_command/start.rb +6 -1
  107. data/lib/rubocop/server/core.rb +23 -8
  108. data/lib/rubocop/version.rb +1 -1
  109. data/lib/rubocop.rb +5 -0
  110. metadata +12 -27
@@ -218,11 +218,9 @@ module RuboCop
218
218
  VARIABLE_ASSIGNMENT_TYPES = %i[casgn cvasgn gvasgn ivasgn lvasgn].freeze
219
219
  ASSIGNMENT_TYPES = VARIABLE_ASSIGNMENT_TYPES + %i[and_asgn or_asgn op_asgn masgn].freeze
220
220
  LINE_LENGTH = 'Layout/LineLength'
221
- INDENTATION_WIDTH = 'Layout/IndentationWidth'
222
221
  ENABLED = 'Enabled'
223
222
  MAX = 'Max'
224
223
  SINGLE_LINE_CONDITIONS_ONLY = 'SingleLineConditionsOnly'
225
- WIDTH = 'Width'
226
224
 
227
225
  # The shovel operator `<<` does not have its own type. It is a `send`
228
226
  # type.
@@ -428,10 +426,6 @@ module RuboCop
428
426
  config.for_cop(LINE_LENGTH)[MAX]
429
427
  end
430
428
 
431
- def indentation_width
432
- config.for_cop(INDENTATION_WIDTH)[WIDTH] || 2
433
- end
434
-
435
429
  def single_line_conditions_only?
436
430
  cop_config[SINGLE_LINE_CONDITIONS_ONLY]
437
431
  end
@@ -145,7 +145,7 @@ module RuboCop
145
145
  end
146
146
 
147
147
  def compact_namespace?(node)
148
- /::/.match?(node.loc.name.source)
148
+ node.loc.name.source.include?('::')
149
149
  end
150
150
 
151
151
  # First checks if the :nodoc: comment is associated with the
@@ -7,6 +7,10 @@ module RuboCop
7
7
  # It can optionally be configured to also require documentation for
8
8
  # non-public methods.
9
9
  #
10
+ # NOTE: This cop allows `initialize` method because `initialize` is
11
+ # a special method called from `new`. In some programming languages
12
+ # they are called constructor to distinguish it from method.
13
+ #
10
14
  # @example
11
15
  #
12
16
  # # bad
@@ -103,6 +107,8 @@ module RuboCop
103
107
  PATTERN
104
108
 
105
109
  def on_def(node)
110
+ return if node.method?(:initialize)
111
+
106
112
  parent = node.parent
107
113
  module_function_node?(parent) ? check(parent) : check(node)
108
114
  end
@@ -118,11 +118,23 @@ module RuboCop
118
118
  end
119
119
 
120
120
  def allowed_receiver?(receiver)
121
- receiver_name = receiver.send_type? ? receiver.method_name.to_s : receiver.source
121
+ receiver_name = receiver_name(receiver)
122
122
 
123
123
  allowed_receivers.include?(receiver_name)
124
124
  end
125
125
 
126
+ def receiver_name(receiver)
127
+ if receiver.send_type?
128
+ if receiver.receiver
129
+ "#{receiver_name(receiver.receiver)}.#{receiver.method_name}"
130
+ else
131
+ receiver.method_name.to_s
132
+ end
133
+ else
134
+ receiver.source
135
+ end
136
+ end
137
+
126
138
  def allowed_receivers
127
139
  cop_config.fetch('AllowedReceivers', [])
128
140
  end
@@ -254,6 +254,7 @@ module RuboCop
254
254
  op = pair_node.loc.operator
255
255
 
256
256
  key_with_hash_rocket = ":#{pair_node.key.source}#{pair_node.inverse_delimiter(true)}"
257
+ key_with_hash_rocket += pair_node.key.source if pair_node.value_omission?
257
258
  corrector.replace(pair_node.key, key_with_hash_rocket)
258
259
  corrector.remove(range_with_surrounding_space(op))
259
260
  end
@@ -21,6 +21,7 @@ module RuboCop
21
21
  # work
22
22
  # end
23
23
  class InfiniteLoop < Base
24
+ include Alignment
24
25
  extend AutoCorrector
25
26
 
26
27
  LEADING_SPACE = /\A(\s*)/.freeze
@@ -106,7 +107,7 @@ module RuboCop
106
107
  else
107
108
  indentation = body.source_range.source_line[LEADING_SPACE]
108
109
 
109
- ['loop do', body.source.gsub(/^/, configured_indent), 'end'].join("\n#{indentation}")
110
+ ['loop do', body.source.gsub(/^/, indentation(node)), 'end'].join("\n#{indentation}")
110
111
  end
111
112
  end
112
113
 
@@ -120,10 +121,6 @@ module RuboCop
120
121
 
121
122
  start_range.join(end_range)
122
123
  end
123
-
124
- def configured_indent
125
- ' ' * config.for_cop('Layout/IndentationWidth')['Width']
126
- end
127
124
  end
128
125
  end
129
126
  end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for usages of `unless` which can be replaced by `if` with inverted condition.
7
+ # Code without `unless` is easier to read, but that is subjective, so this cop
8
+ # is disabled by default.
9
+ #
10
+ # Methods that can be inverted should be defined in `InverseMethods`. Note that
11
+ # the relationship of inverse methods needs to be defined in both directions.
12
+ # For example,
13
+ # InverseMethods:
14
+ # :!=: :==
15
+ # :even?: :odd?
16
+ # :odd?: :even?
17
+ #
18
+ # will suggest both `even?` and `odd?` to be inverted, but only `!=` (and not `==`).
19
+ #
20
+ # @safety
21
+ # This cop is unsafe because it cannot be guaranteed that the method
22
+ # and its inverse method are both defined on receiver, and also are
23
+ # actually inverse of each other.
24
+ #
25
+ # @example
26
+ # # bad (simple condition)
27
+ # foo unless !bar
28
+ # foo unless x != y
29
+ # foo unless x >= 10
30
+ # foo unless x.even?
31
+ #
32
+ # # good
33
+ # foo if bar
34
+ # foo if x == y
35
+ # foo if x < 10
36
+ # foo if x.odd?
37
+ #
38
+ # # bad (complex condition)
39
+ # foo unless x != y || x.even?
40
+ #
41
+ # # good
42
+ # foo if x == y && x.odd?
43
+ #
44
+ # # good (if)
45
+ # foo if !condition
46
+ #
47
+ class InvertibleUnlessCondition < Base
48
+ extend AutoCorrector
49
+
50
+ MSG = 'Favor `if` with inverted condition over `unless`.'
51
+
52
+ def on_if(node)
53
+ return unless node.unless?
54
+
55
+ condition = node.condition
56
+ return unless invertible?(condition)
57
+
58
+ add_offense(node) do |corrector|
59
+ corrector.replace(node.loc.keyword, node.inverse_keyword)
60
+ autocorrect(corrector, condition)
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ def invertible?(node)
67
+ case node.type
68
+ when :begin
69
+ invertible?(node.children.first)
70
+ when :send
71
+ return if inheritance_check?(node)
72
+
73
+ node.method?(:!) || inverse_methods.key?(node.method_name)
74
+ when :or, :and
75
+ invertible?(node.lhs) && invertible?(node.rhs)
76
+ else
77
+ false
78
+ end
79
+ end
80
+
81
+ def inheritance_check?(node)
82
+ argument = node.first_argument
83
+ node.method?(:<) &&
84
+ (argument.const_type? && argument.short_name.to_s.upcase != argument.short_name.to_s)
85
+ end
86
+
87
+ def autocorrect(corrector, node)
88
+ case node.type
89
+ when :begin
90
+ autocorrect(corrector, node.children.first)
91
+ when :send
92
+ autocorrect_send_node(corrector, node)
93
+ when :or, :and
94
+ corrector.replace(node.loc.operator, node.inverse_operator)
95
+ autocorrect(corrector, node.lhs)
96
+ autocorrect(corrector, node.rhs)
97
+ end
98
+ end
99
+
100
+ def autocorrect_send_node(corrector, node)
101
+ if node.method?(:!)
102
+ corrector.remove(node.loc.selector)
103
+ else
104
+ corrector.replace(node.loc.selector, inverse_methods[node.method_name])
105
+ end
106
+ end
107
+
108
+ def inverse_methods
109
+ @inverse_methods ||= cop_config['InverseMethods']
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -13,8 +13,7 @@ module RuboCop
13
13
 
14
14
  private
15
15
 
16
- # rubocop:disable Metrics/PerceivedComplexity
17
- def omit_parentheses(node)
16
+ def omit_parentheses(node) # rubocop:disable Metrics/PerceivedComplexity
18
17
  return unless node.parenthesized?
19
18
  return if inside_endless_method_def?(node)
20
19
  return if require_parentheses_for_hash_value_omission?(node)
@@ -28,7 +27,6 @@ module RuboCop
28
27
  autocorrect(corrector, node)
29
28
  end
30
29
  end
31
- # rubocop:enable Metrics/PerceivedComplexity
32
30
 
33
31
  def autocorrect(corrector, node)
34
32
  if parentheses_at_the_end_of_multiline_call?(node)
@@ -90,7 +88,7 @@ module RuboCop
90
88
  .end_with?('(')
91
89
  end
92
90
 
93
- def legitimate_call_with_parentheses?(node)
91
+ def legitimate_call_with_parentheses?(node) # rubocop:disable Metrics/PerceivedComplexity
94
92
  call_in_literals?(node) ||
95
93
  call_with_ambiguous_arguments?(node) ||
96
94
  call_in_logical_operators?(node) ||
@@ -98,24 +96,28 @@ module RuboCop
98
96
  call_in_single_line_inheritance?(node) ||
99
97
  allowed_multiline_call_with_parentheses?(node) ||
100
98
  allowed_chained_call_with_parentheses?(node) ||
101
- assignment_in_condition?(node)
99
+ assignment_in_condition?(node) ||
100
+ forwards_anonymous_rest_arguments?(node)
102
101
  end
103
102
 
104
103
  def call_in_literals?(node)
105
- node.parent &&
106
- (node.parent.pair_type? ||
107
- node.parent.array_type? ||
108
- node.parent.range_type? ||
109
- splat?(node.parent) ||
110
- ternary_if?(node.parent))
104
+ parent = node.parent&.block_type? ? node.parent.parent : node.parent
105
+ return unless parent
106
+
107
+ parent.pair_type? ||
108
+ parent.array_type? ||
109
+ parent.range_type? ||
110
+ splat?(parent) ||
111
+ ternary_if?(parent)
111
112
  end
112
113
 
113
114
  def call_in_logical_operators?(node)
114
115
  parent = node.parent&.block_type? ? node.parent.parent : node.parent
115
- parent &&
116
- (logical_operator?(parent) ||
116
+ return unless parent
117
+
118
+ logical_operator?(parent) ||
117
119
  (parent.send_type? &&
118
- parent.arguments.any? { |argument| logical_operator?(argument) }))
120
+ parent.arguments.any? { |argument| logical_operator?(argument) })
119
121
  end
120
122
 
121
123
  def call_in_optional_arguments?(node)
@@ -213,6 +215,13 @@ module RuboCop
213
215
 
214
216
  parent.assignment? && (grandparent.conditional? || grandparent.when_type?)
215
217
  end
218
+
219
+ def forwards_anonymous_rest_arguments?(node)
220
+ return false unless (last_argument = node.last_argument)
221
+ return true if last_argument.forwarded_restarg_type?
222
+
223
+ last_argument.hash_type? && last_argument.children.first&.forwarded_kwrestarg_type?
224
+ end
216
225
  end
217
226
  # rubocop:enable Metrics/ModuleLength, Metrics/CyclomaticComplexity
218
227
  end
@@ -62,6 +62,8 @@ module RuboCop
62
62
  end
63
63
 
64
64
  def same_name_assignment?(node)
65
+ return false if node.receiver
66
+
65
67
  any_assignment?(node) do |asgn_node|
66
68
  next variable_in_mass_assignment?(node.method_name, asgn_node) if asgn_node.masgn_type?
67
69
 
@@ -36,6 +36,7 @@ module RuboCop
36
36
  #
37
37
  class MinMaxComparison < Base
38
38
  extend AutoCorrector
39
+ include RangeHelp
39
40
 
40
41
  MSG = 'Use `%<prefer>s` instead.'
41
42
  GRATER_OPERATORS = %i[> >=].freeze
@@ -54,7 +55,7 @@ module RuboCop
54
55
  replacement = "[#{lhs.source}, #{rhs.source}].#{preferred_method}"
55
56
 
56
57
  add_offense(node, message: format(MSG, prefer: replacement)) do |corrector|
57
- corrector.replace(node, replacement)
58
+ autocorrect(corrector, node, replacement)
58
59
  end
59
60
  end
60
61
 
@@ -67,6 +68,15 @@ module RuboCop
67
68
  LESS_OPERATORS.include?(operator) ? 'max' : 'min'
68
69
  end
69
70
  end
71
+
72
+ def autocorrect(corrector, node, replacement)
73
+ if node.elsif?
74
+ corrector.remove(range_between(node.parent.loc.else.begin_pos, node.loc.else.begin_pos))
75
+ corrector.replace(node.else_branch, replacement)
76
+ else
77
+ corrector.replace(node, replacement)
78
+ end
79
+ end
70
80
  end
71
81
  end
72
82
  end
@@ -99,6 +99,7 @@ module RuboCop
99
99
  class MissingElse < Base
100
100
  include OnNormalIfUnless
101
101
  include ConfigurableEnforcedStyle
102
+ extend AutoCorrector
102
103
 
103
104
  MSG = '`%<type>s` condition requires an `else`-clause.'
104
105
  MSG_NIL = '`%<type>s` condition requires an `else`-clause with `nil` in it.'
@@ -126,7 +127,9 @@ module RuboCop
126
127
  def check(node)
127
128
  return if node.else?
128
129
 
129
- add_offense(node, message: format(message_template, type: node.type))
130
+ add_offense(node, message: format(message_template, type: node.type)) do |corrector|
131
+ autocorrect(corrector, node)
132
+ end
130
133
  end
131
134
 
132
135
  def message_template
@@ -140,6 +143,15 @@ module RuboCop
140
143
  end
141
144
  end
142
145
 
146
+ def autocorrect(corrector, node)
147
+ case empty_else_style
148
+ when :empty
149
+ corrector.insert_before(node.loc.end, 'else; nil; ')
150
+ when :nil
151
+ corrector.insert_before(node.loc.end, 'else; ')
152
+ end
153
+ end
154
+
143
155
  def if_style?
144
156
  style == :if
145
157
  end
@@ -40,10 +40,6 @@ module RuboCop
40
40
  [condition, indented_body, indented_end].join("\n")
41
41
  end
42
42
 
43
- def configured_indentation_width
44
- super || 2
45
- end
46
-
47
43
  def indented_body(body, node)
48
44
  body_source = "#{offset(node)}#{body.source}"
49
45
  body_source.each_line.map do |line|
@@ -31,6 +31,7 @@ module RuboCop
31
31
  # baz
32
32
  # )
33
33
  class MultilineMemoization < Base
34
+ include Alignment
34
35
  include ConfigurableEnforcedStyle
35
36
  extend AutoCorrector
36
37
 
@@ -75,11 +76,10 @@ module RuboCop
75
76
  end
76
77
 
77
78
  def keyword_begin_str(node, node_buf)
78
- indent = config.for_cop('Layout/IndentationWidth')['Width'] || 2
79
79
  if node_buf.source[node.loc.begin.end_pos] == "\n"
80
80
  'begin'
81
81
  else
82
- "begin\n#{' ' * (node.loc.column + indent)}"
82
+ "begin\n#{' ' * (node.loc.column + configured_indentation_width)}"
83
83
  end
84
84
  end
85
85
 
@@ -34,6 +34,7 @@ module RuboCop
34
34
  # return cond ? b : c
35
35
  #
36
36
  class MultilineTernaryOperator < Base
37
+ include CommentsHelp
37
38
  extend AutoCorrector
38
39
 
39
40
  MSG_IF = 'Avoid multi-line ternary operators, use `if` or `unless` instead.'
@@ -46,9 +47,7 @@ module RuboCop
46
47
  message = enforce_single_line_ternary_operator?(node) ? MSG_SINGLE_LINE : MSG_IF
47
48
 
48
49
  add_offense(node, message: message) do |corrector|
49
- next unless offense?(node)
50
-
51
- corrector.replace(node, replacement(node))
50
+ autocorrect(corrector, node)
52
51
  end
53
52
  end
54
53
 
@@ -58,6 +57,16 @@ module RuboCop
58
57
  node.ternary? && node.multiline?
59
58
  end
60
59
 
60
+ def autocorrect(corrector, node)
61
+ return unless offense?(node)
62
+
63
+ corrector.replace(node, replacement(node))
64
+ return unless (parent = node.parent)
65
+ return unless (comments_in_condition = comments_in_condition(node))
66
+
67
+ corrector.insert_before(parent, comments_in_condition)
68
+ end
69
+
61
70
  def replacement(node)
62
71
  if enforce_single_line_ternary_operator?(node)
63
72
  "#{node.condition.source} ? #{node.if_branch.source} : #{node.else_branch.source}"
@@ -72,6 +81,12 @@ module RuboCop
72
81
  end
73
82
  end
74
83
 
84
+ def comments_in_condition(node)
85
+ comments_in_range(node).map do |comment|
86
+ "#{comment.loc.expression.source}\n"
87
+ end.join
88
+ end
89
+
75
90
  def enforce_single_line_ternary_operator?(node)
76
91
  SINGLE_LINE_TYPES.include?(node.parent&.type) && !use_assignment_method?(node.parent)
77
92
  end
@@ -103,11 +103,7 @@ module RuboCop
103
103
  if node.if_branch.nil?
104
104
  corrector.remove(range_by_whole_lines(node.loc.else, include_final_newline: true))
105
105
  else
106
- if_range = if_range(node)
107
- else_range = else_range(node)
108
-
109
- corrector.replace(if_range, else_range.source)
110
- corrector.replace(else_range, if_range.source)
106
+ corrector.swap(if_range(node), else_range(node))
111
107
  end
112
108
  end
113
109
 
@@ -12,10 +12,11 @@ module RuboCop
12
12
  #
13
13
  # @example Max: 1 (default)
14
14
  # # bad
15
- # foo { _1.call(_2, _3, _4) }
15
+ # use_multiple_numbered_parameters { _1.call(_2, _3, _4) }
16
16
  #
17
17
  # # good
18
- # foo { do_something(_1) }
18
+ # array.each { use_array_element_as_numbered_parameter(_1) }
19
+ # hash.each { use_only_hash_value_as_numbered_parameter(_2) }
19
20
  class NumberedParametersLimit < Base
20
21
  extend TargetRubyVersion
21
22
  extend ExcludeLimit
@@ -26,9 +27,10 @@ module RuboCop
26
27
  exclude_limit 'Max'
27
28
 
28
29
  MSG = 'Avoid using more than %<max>i numbered %<parameter>s; %<count>i detected.'
30
+ NUMBERED_PARAMETER_PATTERN = /\A_[1-9]\z/.freeze
29
31
 
30
32
  def on_numblock(node)
31
- _send_node, param_count, * = *node
33
+ param_count = numbered_parameter_nodes(node).uniq.count
32
34
  return if param_count <= max_count
33
35
 
34
36
  parameter = max_count > 1 ? 'parameters' : 'parameter'
@@ -38,6 +40,12 @@ module RuboCop
38
40
 
39
41
  private
40
42
 
43
+ def numbered_parameter_nodes(node)
44
+ node.each_descendant(:lvar).select do |lvar_node|
45
+ lvar_node.source.match?(NUMBERED_PARAMETER_PATTERN)
46
+ end
47
+ end
48
+
41
49
  def max_count
42
50
  max = cop_config.fetch('Max', DEFAULT_MAX_VALUE)
43
51
 
@@ -31,6 +31,7 @@ module RuboCop
31
31
  # baz
32
32
  # end
33
33
  class OneLineConditional < Base
34
+ include Alignment
34
35
  include ConfigurableEnforcedStyle
35
36
  include OnNormalIfUnless
36
37
  extend AutoCorrector
@@ -57,7 +58,7 @@ module RuboCop
57
58
 
58
59
  def autocorrect(corrector, node)
59
60
  if always_multiline? || cannot_replace_to_ternary?(node)
60
- IfThenCorrector.new(node, indentation: indentation_width).call(corrector)
61
+ IfThenCorrector.new(node, indentation: configured_indentation_width).call(corrector)
61
62
  else
62
63
  corrector.replace(node, ternary_correction(node))
63
64
  end
@@ -67,7 +68,7 @@ module RuboCop
67
68
  replaced_node = ternary_replacement(node)
68
69
 
69
70
  return replaced_node unless node.parent
70
- return "(#{replaced_node})" if %i[and or].include?(node.parent.type)
71
+ return "(#{replaced_node})" if node.parent.operator_keyword?
71
72
  return "(#{replaced_node})" if node.parent.send_type? && node.parent.operator_method?
72
73
 
73
74
  replaced_node
@@ -116,10 +117,6 @@ module RuboCop
116
117
 
117
118
  node.respond_to?(:arguments?) && node.arguments? && !node.parenthesized_call?
118
119
  end
119
-
120
- def indentation_width
121
- @config.for_cop('Layout/IndentationWidth')['Width']
122
- end
123
120
  end
124
121
  end
125
122
  end
@@ -25,10 +25,10 @@ module RuboCop
25
25
 
26
26
  def on_send(node)
27
27
  return unless (dot = node.loc.dot)
28
- return if node.receiver.const_type?
28
+ return if node.receiver.const_type? || !node.arguments.one?
29
29
 
30
30
  _lhs, _op, rhs = *node
31
- return if rhs.nil? || rhs.children.first
31
+ return if !rhs || method_call_with_parenthesized_arg?(rhs) || anonymous_forwarding?(rhs)
32
32
 
33
33
  add_offense(dot) do |corrector|
34
34
  wrap_in_parentheses_if_chained(corrector, node)
@@ -38,6 +38,20 @@ module RuboCop
38
38
 
39
39
  private
40
40
 
41
+ # Checks for an acceptable case of `foo.+(bar).baz`.
42
+ def method_call_with_parenthesized_arg?(argument)
43
+ return false unless argument.parent.parent&.send_type?
44
+
45
+ argument.children.first && argument.parent.parenthesized?
46
+ end
47
+
48
+ def anonymous_forwarding?(argument)
49
+ return true if argument.forwarded_args_type? || argument.forwarded_restarg_type?
50
+ return true if argument.hash_type? && argument.children.first&.forwarded_kwrestarg_type?
51
+
52
+ argument.block_pass_type? && argument.source == '&'
53
+ end
54
+
41
55
  def wrap_in_parentheses_if_chained(corrector, node)
42
56
  return unless node.parent&.call_type?
43
57
 
@@ -172,7 +172,9 @@ module RuboCop
172
172
  end
173
173
 
174
174
  def modifier_statement?(node)
175
- node && %i[if while until].include?(node.type) && node.modifier_form?
175
+ return false unless node
176
+
177
+ node.basic_conditional? && node.modifier_form?
176
178
  end
177
179
 
178
180
  # An internal class for correcting parallel assignment
@@ -36,6 +36,9 @@ module RuboCop
36
36
 
37
37
  MSG = 'Use double pipes `||` instead.'
38
38
  REDUNDANT_CONDITION = 'This condition is not needed.'
39
+ ARGUMENT_WITH_OPERATOR_TYPES = %i[
40
+ splat block_pass forwarded_restarg forwarded_kwrestarg forwarded_args
41
+ ].freeze
39
42
 
40
43
  def on_if(node)
41
44
  return if node.elsif_conditional?
@@ -150,13 +153,25 @@ module RuboCop
150
153
  end
151
154
 
152
155
  def single_argument_method?(node)
153
- node.send_type? && !node.method?(:[]) && node.arguments.one?
156
+ return false if !node.send_type? || node.method?(:[]) || !node.arguments.one?
157
+
158
+ !argument_with_operator?(node.first_argument)
154
159
  end
155
160
 
156
161
  def same_method?(if_branch, else_branch)
157
162
  if_branch.method?(else_branch.method_name) && if_branch.receiver == else_branch.receiver
158
163
  end
159
164
 
165
+ # If the argument is using an operator, it is an invalid syntax.
166
+ # e.g. `foo || *bar`, `foo || **bar`, and `foo || &bar`.
167
+ def argument_with_operator?(argument)
168
+ return true if ARGUMENT_WITH_OPERATOR_TYPES.include?(argument.type)
169
+ return false unless argument.hash_type?
170
+ return false unless (node = argument.children.first)
171
+
172
+ node.kwsplat_type? || node.forwarded_kwrestarg_type?
173
+ end
174
+
160
175
  def if_source(if_branch, arithmetic_operation)
161
176
  if branches_have_method?(if_branch.parent) && if_branch.parenthesized?
162
177
  if_branch.source.delete_suffix(')')
@@ -86,10 +86,6 @@ module RuboCop
86
86
  def indented_else_node(expression, node)
87
87
  "else\n#{indentation(node)}#{expression}"
88
88
  end
89
-
90
- def configured_indentation_width
91
- super || 2
92
- end
93
89
  end
94
90
  end
95
91
  end