rubocop 1.43.0 → 1.45.1

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 (102) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +64 -29
  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 +15 -1
  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/utils/abc_size_calculator.rb +2 -5
  44. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  45. data/lib/rubocop/cop/mixin/allowed_methods.rb +3 -1
  46. data/lib/rubocop/cop/mixin/comments_help.rb +5 -3
  47. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +51 -25
  48. data/lib/rubocop/cop/mixin/line_length_help.rb +3 -1
  49. data/lib/rubocop/cop/mixin/surrounding_space.rb +3 -3
  50. data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
  51. data/lib/rubocop/cop/naming/block_forwarding.rb +4 -0
  52. data/lib/rubocop/cop/naming/class_and_module_camel_case.rb +1 -1
  53. data/lib/rubocop/cop/registry.rb +12 -7
  54. data/lib/rubocop/cop/style/access_modifier_declarations.rb +26 -11
  55. data/lib/rubocop/cop/style/arguments_forwarding.rb +1 -0
  56. data/lib/rubocop/cop/style/block_delimiters.rb +8 -2
  57. data/lib/rubocop/cop/style/class_and_module_children.rb +3 -10
  58. data/lib/rubocop/cop/style/command_literal.rb +1 -1
  59. data/lib/rubocop/cop/style/comparable_clamp.rb +125 -0
  60. data/lib/rubocop/cop/style/conditional_assignment.rb +0 -6
  61. data/lib/rubocop/cop/style/documentation.rb +1 -1
  62. data/lib/rubocop/cop/style/documentation_method.rb +6 -0
  63. data/lib/rubocop/cop/style/infinite_loop.rb +2 -5
  64. data/lib/rubocop/cop/style/invertible_unless_condition.rb +114 -0
  65. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +11 -5
  66. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +2 -0
  67. data/lib/rubocop/cop/style/min_max_comparison.rb +11 -1
  68. data/lib/rubocop/cop/style/multiline_if_modifier.rb +0 -4
  69. data/lib/rubocop/cop/style/multiline_memoization.rb +2 -2
  70. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +18 -3
  71. data/lib/rubocop/cop/style/negated_if_else_condition.rb +1 -5
  72. data/lib/rubocop/cop/style/numbered_parameters_limit.rb +11 -3
  73. data/lib/rubocop/cop/style/one_line_conditional.rb +3 -6
  74. data/lib/rubocop/cop/style/operator_method_call.rb +2 -2
  75. data/lib/rubocop/cop/style/parallel_assignment.rb +3 -1
  76. data/lib/rubocop/cop/style/redundant_condition.rb +16 -1
  77. data/lib/rubocop/cop/style/redundant_conditional.rb +0 -4
  78. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +16 -10
  79. data/lib/rubocop/cop/style/redundant_heredoc_delimiter_quotes.rb +58 -0
  80. data/lib/rubocop/cop/style/require_order.rb +2 -9
  81. data/lib/rubocop/cop/style/self_assignment.rb +2 -2
  82. data/lib/rubocop/cop/style/semicolon.rb +24 -2
  83. data/lib/rubocop/cop/style/symbol_array.rb +1 -1
  84. data/lib/rubocop/cop/style/word_array.rb +1 -1
  85. data/lib/rubocop/cop/style/yoda_condition.rb +12 -5
  86. data/lib/rubocop/cop/style/yoda_expression.rb +11 -2
  87. data/lib/rubocop/cop/team.rb +19 -14
  88. data/lib/rubocop/cop/variable_force/scope.rb +3 -3
  89. data/lib/rubocop/cop/variable_force/variable_table.rb +3 -1
  90. data/lib/rubocop/cop/variable_force.rb +1 -1
  91. data/lib/rubocop/formatter.rb +0 -1
  92. data/lib/rubocop/options.rb +22 -1
  93. data/lib/rubocop/rspec/expect_offense.rb +6 -4
  94. data/lib/rubocop/runner.rb +40 -4
  95. data/lib/rubocop/server/cache.rb +10 -3
  96. data/lib/rubocop/server/cli.rb +37 -18
  97. data/lib/rubocop/server/client_command/exec.rb +1 -1
  98. data/lib/rubocop/server/client_command/start.rb +6 -1
  99. data/lib/rubocop/server/core.rb +23 -8
  100. data/lib/rubocop/version.rb +1 -1
  101. data/lib/rubocop.rb +4 -0
  102. metadata +11 -27
@@ -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,7 +96,8 @@ 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)
@@ -216,6 +215,13 @@ module RuboCop
216
215
 
217
216
  parent.assignment? && (grandparent.conditional? || grandparent.when_type?)
218
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
219
225
  end
220
226
  # rubocop:enable Metrics/ModuleLength, Metrics/CyclomaticComplexity
221
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
@@ -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,7 +25,7 @@ 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
31
  return if !rhs || method_call_with_parenthesized_arg?(rhs) || anonymous_forwarding?(rhs)
@@ -47,7 +47,7 @@ module RuboCop
47
47
 
48
48
  def anonymous_forwarding?(argument)
49
49
  return true if argument.forwarded_args_type? || argument.forwarded_restarg_type?
50
- return true if argument.children.first&.forwarded_kwrestarg_type?
50
+ return true if argument.hash_type? && argument.children.first&.forwarded_kwrestarg_type?
51
51
 
52
52
  argument.block_pass_type? && argument.source == '&'
53
53
  end
@@ -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
@@ -18,21 +18,27 @@ module RuboCop
18
18
 
19
19
  MSG = 'Remove the redundant double splat and braces, use keyword arguments directly.'
20
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
21
  def on_hash(node)
27
22
  return if node.pairs.empty? || node.pairs.any?(&:hash_rocket?)
23
+ return unless (parent = node.parent)
24
+ return unless parent.kwsplat_type?
28
25
 
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.children.map(&:source).join(', '))
26
+ add_offense(parent) do |corrector|
27
+ corrector.remove(parent.loc.operator)
28
+ corrector.remove(opening_brace(node))
29
+ corrector.remove(closing_brace(node))
34
30
  end
35
31
  end
32
+
33
+ private
34
+
35
+ def opening_brace(node)
36
+ node.loc.begin.join(node.children.first.loc.expression.begin)
37
+ end
38
+
39
+ def closing_brace(node)
40
+ node.children.last.loc.expression.end.join(node.loc.end)
41
+ end
36
42
  end
37
43
  end
38
44
  end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for redundant heredoc delimiter quotes.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # do_something(<<~'EOS')
12
+ # no string interpolation style text
13
+ # EOS
14
+ #
15
+ # # good
16
+ # do_something(<<~EOS)
17
+ # no string interpolation style text
18
+ # EOS
19
+ #
20
+ # do_something(<<~'EOS')
21
+ # #{string_interpolation_style_text_not_evaluated}
22
+ # EOS
23
+ #
24
+ # do_something(<<~'EOS')
25
+ # Preserve \
26
+ # newlines
27
+ # EOS
28
+ #
29
+ class RedundantHeredocDelimiterQuotes < Base
30
+ include Heredoc
31
+ extend AutoCorrector
32
+
33
+ MSG = 'Remove the redundant heredoc delimiter quotes, use `%<replacement>s` instead.'
34
+ STRING_INTERPOLATION_OR_ESCAPED_CHARACTER_PATTERN = /#(\{|@|\$)|\\/.freeze
35
+
36
+ def on_heredoc(node)
37
+ return if need_heredoc_delimiter_quotes?(node)
38
+
39
+ replacement = "#{heredoc_type(node)}#{delimiter_string(node)}"
40
+
41
+ add_offense(node, message: format(MSG, replacement: replacement)) do |corrector|
42
+ corrector.replace(node, replacement)
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def need_heredoc_delimiter_quotes?(node)
49
+ heredoc_delimiter = node.source.delete(heredoc_type(node))
50
+ return true unless heredoc_delimiter.start_with?("'", '"')
51
+
52
+ node.loc.heredoc_end.source.strip.match?(/\W/) ||
53
+ node.loc.heredoc_body.source.match?(STRING_INTERPOLATION_OR_ESCAPED_CHARACTER_PATTERN)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -88,10 +88,9 @@ module RuboCop
88
88
  return unless previous_older_sibling
89
89
 
90
90
  add_offense(node, message: format(MSG, name: node.method_name)) do |corrector|
91
- swap(
91
+ corrector.swap(
92
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
93
+ range_with_comments_and_lines(node.parent.if_type? ? node.parent : node)
95
94
  )
96
95
  end
97
96
  end
@@ -130,12 +129,6 @@ module RuboCop
130
129
  end_pos: node2.location.expression.end_pos
131
130
  ).source.include?("\n\n")
132
131
  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
132
  end
140
133
  end
141
134
  end
@@ -42,7 +42,7 @@ module RuboCop
42
42
 
43
43
  if rhs.send_type?
44
44
  check_send_node(node, rhs, var_name, var_type)
45
- elsif %i[and or].include?(rhs.type)
45
+ elsif rhs.operator_keyword?
46
46
  check_boolean_node(node, rhs, var_name, var_type)
47
47
  end
48
48
  end
@@ -76,7 +76,7 @@ module RuboCop
76
76
 
77
77
  if rhs.send_type?
78
78
  autocorrect_send_node(corrector, node, rhs)
79
- elsif %i[and or].include?(rhs.type)
79
+ elsif rhs.operator_keyword?
80
80
  autocorrect_boolean_node(corrector, node, rhs)
81
81
  end
82
82
  end
@@ -72,8 +72,10 @@ module RuboCop
72
72
 
73
73
  def each_semicolon
74
74
  tokens_for_lines.each do |line, tokens|
75
- yield line, tokens.last.column, tokens[-2] if tokens.last.semicolon?
76
- yield line, tokens.first.column if tokens.first.semicolon?
75
+ semicolon_pos = semicolon_position(tokens)
76
+ after_expr_pos = semicolon_pos == -1 ? -2 : semicolon_pos
77
+
78
+ yield line, tokens[semicolon_pos].column, tokens[after_expr_pos] if semicolon_pos
77
79
  end
78
80
  end
79
81
 
@@ -81,6 +83,26 @@ module RuboCop
81
83
  processed_source.tokens.group_by(&:line)
82
84
  end
83
85
 
86
+ def semicolon_position(tokens)
87
+ if tokens.last.semicolon?
88
+ -1
89
+ elsif tokens.first.semicolon?
90
+ 0
91
+ elsif exist_semicolon_before_right_curly_brace?(tokens)
92
+ -3
93
+ elsif exist_semicolon_after_left_curly_brace?(tokens)
94
+ 2
95
+ end
96
+ end
97
+
98
+ def exist_semicolon_before_right_curly_brace?(tokens)
99
+ tokens[-2]&.right_curly_brace? && tokens[-3]&.semicolon?
100
+ end
101
+
102
+ def exist_semicolon_after_left_curly_brace?(tokens)
103
+ tokens[1]&.left_curly_brace? && tokens[2]&.semicolon?
104
+ end
105
+
84
106
  def register_semicolon(line, column, after_expression, token_before_semicolon = nil)
85
107
  range = source_range(processed_source.buffer, line, column)
86
108
 
@@ -60,7 +60,7 @@ module RuboCop
60
60
  def symbols_contain_spaces?(node)
61
61
  node.children.any? do |sym|
62
62
  content, = *sym
63
- / /.match?(content)
63
+ content.to_s.include?(' ')
64
64
  end
65
65
  end
66
66
 
@@ -110,7 +110,7 @@ module RuboCop
110
110
  string = s.str_content.dup.force_encoding(::Encoding::UTF_8)
111
111
  !string.valid_encoding? ||
112
112
  (complex_regex && !complex_regex.match?(string)) ||
113
- / /.match?(string)
113
+ string.include?(' ')
114
114
  end
115
115
  end
116
116