rubocop 1.44.1 → 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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +16 -31
  4. data/lib/rubocop/cli.rb +54 -8
  5. data/lib/rubocop/config_loader_resolver.rb +3 -4
  6. data/lib/rubocop/cop/base.rb +27 -9
  7. data/lib/rubocop/cop/commissioner.rb +8 -2
  8. data/lib/rubocop/cop/cop.rb +23 -3
  9. data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
  10. data/lib/rubocop/cop/layout/class_structure.rb +2 -16
  11. data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -1
  12. data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
  13. data/lib/rubocop/cop/layout/space_around_operators.rb +1 -1
  14. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +11 -11
  15. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +4 -4
  16. data/lib/rubocop/cop/layout/space_inside_string_interpolation.rb +5 -4
  17. data/lib/rubocop/cop/lint/debugger.rb +8 -27
  18. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +4 -1
  19. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +1 -1
  20. data/lib/rubocop/cop/lint/nested_method_definition.rb +8 -5
  21. data/lib/rubocop/cop/lint/useless_access_modifier.rb +7 -4
  22. data/lib/rubocop/cop/metrics/block_length.rb +1 -1
  23. data/lib/rubocop/cop/mixin/allowed_methods.rb +3 -1
  24. data/lib/rubocop/cop/mixin/comments_help.rb +5 -3
  25. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +13 -9
  26. data/lib/rubocop/cop/mixin/surrounding_space.rb +3 -3
  27. data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
  28. data/lib/rubocop/cop/naming/class_and_module_camel_case.rb +1 -1
  29. data/lib/rubocop/cop/style/access_modifier_declarations.rb +8 -1
  30. data/lib/rubocop/cop/style/arguments_forwarding.rb +1 -0
  31. data/lib/rubocop/cop/style/class_and_module_children.rb +1 -1
  32. data/lib/rubocop/cop/style/command_literal.rb +1 -1
  33. data/lib/rubocop/cop/style/documentation.rb +1 -1
  34. data/lib/rubocop/cop/style/documentation_method.rb +6 -0
  35. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +18 -3
  36. data/lib/rubocop/cop/style/numbered_parameters_limit.rb +11 -3
  37. data/lib/rubocop/cop/style/operator_method_call.rb +1 -1
  38. data/lib/rubocop/cop/style/redundant_condition.rb +16 -1
  39. data/lib/rubocop/cop/style/redundant_heredoc_delimiter_quotes.rb +58 -0
  40. data/lib/rubocop/cop/style/symbol_array.rb +1 -1
  41. data/lib/rubocop/cop/style/word_array.rb +1 -1
  42. data/lib/rubocop/cop/style/yoda_condition.rb +12 -5
  43. data/lib/rubocop/cop/style/yoda_expression.rb +11 -2
  44. data/lib/rubocop/cop/team.rb +19 -14
  45. data/lib/rubocop/cop/variable_force/scope.rb +3 -3
  46. data/lib/rubocop/options.rb +22 -1
  47. data/lib/rubocop/runner.rb +40 -4
  48. data/lib/rubocop/server/cache.rb +7 -2
  49. data/lib/rubocop/server/cli.rb +37 -18
  50. data/lib/rubocop/server/client_command/exec.rb +1 -1
  51. data/lib/rubocop/server/client_command/start.rb +6 -1
  52. data/lib/rubocop/server/core.rb +23 -8
  53. data/lib/rubocop/version.rb +1 -1
  54. data/lib/rubocop.rb +1 -0
  55. metadata +8 -27
@@ -31,13 +31,14 @@ module RuboCop
31
31
  def on_interpolation(begin_node)
32
32
  return if begin_node.multiline?
33
33
 
34
- delims = delimiters(begin_node)
35
- return if empty_brackets?(*delims)
34
+ tokens = processed_source.tokens_within(begin_node)
35
+ left, right = delimiters(begin_node)
36
+ return if empty_brackets?(left, right, tokens: tokens)
36
37
 
37
38
  if style == :no_space
38
- no_space_offenses(begin_node, *delims, NO_SPACE_MSG)
39
+ no_space_offenses(begin_node, left, right, NO_SPACE_MSG)
39
40
  else
40
- space_offenses(begin_node, *delims, SPACE_MSG)
41
+ space_offenses(begin_node, left, right, SPACE_MSG)
41
42
  end
42
43
  end
43
44
 
@@ -82,44 +82,25 @@ module RuboCop
82
82
  def debugger_methods
83
83
  @debugger_methods ||= begin
84
84
  config = cop_config.fetch('DebuggerMethods', [])
85
- values = config.is_a?(Array) ? config : config.values.flatten
86
- values.map do |v|
87
- next unless v
88
-
89
- *receiver, method_name = v.split('.')
90
- {
91
- receiver: receiver.empty? ? nil : receiver.map(&:to_sym),
92
- method_name: method_name.to_sym
93
- }
94
- end.compact
85
+ config.is_a?(Array) ? config : config.values.flatten
95
86
  end
96
87
  end
97
88
 
98
89
  def debugger_method?(send_node)
99
- method_name = send_node.method_name
100
-
101
- debugger_methods.any? do |method|
102
- next unless method[:method_name] == method_name
90
+ return if send_node.parent&.send_type? && send_node.parent.receiver == send_node
103
91
 
104
- if method[:receiver].nil?
105
- send_node.receiver.nil?
106
- else
107
- method[:receiver] == receiver_chain(send_node)
108
- end
109
- end
92
+ debugger_methods.include?(chained_method_name(send_node))
110
93
  end
111
94
 
112
- def receiver_chain(send_node)
113
- receivers = []
95
+ def chained_method_name(send_node)
96
+ chained_method_name = send_node.method_name.to_s
114
97
  receiver = send_node.receiver
115
-
116
98
  while receiver
117
- name = receiver.send_type? ? receiver.method_name : receiver.const_name&.to_sym
118
- receivers.unshift(name)
99
+ name = receiver.send_type? ? receiver.method_name : receiver.const_name
100
+ chained_method_name = "#{name}.#{chained_method_name}"
119
101
  receiver = receiver.receiver
120
102
  end
121
-
122
- receivers
103
+ chained_method_name
123
104
  end
124
105
  end
125
106
  end
@@ -80,7 +80,10 @@ module RuboCop
80
80
  num_of_format_args, num_of_expected_fields = count_matches(node)
81
81
 
82
82
  return false if num_of_format_args == :unknown
83
- return false if num_of_expected_fields.zero? && node.child_nodes.first.dstr_type?
83
+
84
+ first_arg = node.first_argument
85
+ return false if num_of_expected_fields.zero? &&
86
+ (first_arg.dstr_type? || first_arg.array_type?)
84
87
 
85
88
  matched_arguments_count?(num_of_expected_fields, num_of_format_args)
86
89
  end
@@ -81,7 +81,7 @@ module RuboCop
81
81
  end
82
82
 
83
83
  def display_str(node)
84
- if /\n/.match?(node.source)
84
+ if node.source.include?("\n")
85
85
  str_content(node).inspect
86
86
  else
87
87
  node.source
@@ -108,7 +108,7 @@ module RuboCop
108
108
  return unless def_ancestor
109
109
 
110
110
  within_scoping_def =
111
- node.each_ancestor(:block, :sclass).any? do |ancestor|
111
+ node.each_ancestor(:block, :numblock, :sclass).any? do |ancestor|
112
112
  scoping_method_call?(ancestor)
113
113
  end
114
114
 
@@ -120,7 +120,7 @@ module RuboCop
120
120
 
121
121
  def scoping_method_call?(child)
122
122
  child.sclass_type? || eval_call?(child) || exec_call?(child) ||
123
- class_or_module_or_struct_new_call?(child) || allowed_method_name?(child)
123
+ class_constructor?(child) || allowed_method_name?(child)
124
124
  end
125
125
 
126
126
  def allowed_method_name?(node)
@@ -139,9 +139,12 @@ module RuboCop
139
139
  (block (send _ {:instance_exec :class_exec :module_exec} ...) ...)
140
140
  PATTERN
141
141
 
142
- # @!method class_or_module_or_struct_new_call?(node)
143
- def_node_matcher :class_or_module_or_struct_new_call?, <<~PATTERN
144
- (block (send (const {nil? cbase} {:Class :Module :Struct}) :new ...) ...)
142
+ # @!method class_constructor?(node)
143
+ def_node_matcher :class_constructor?, <<~PATTERN
144
+ ({block numblock} {
145
+ (send (const {nil? cbase} {:Class :Module :Struct}) :new ...)
146
+ (send (const {nil? cbase} :Data) :define ...)
147
+ } ...)
145
148
  PATTERN
146
149
  end
147
150
  end
@@ -167,9 +167,12 @@ module RuboCop
167
167
  ({block numblock} (send _ {:class_eval :instance_eval}) ...)
168
168
  PATTERN
169
169
 
170
- # @!method class_or_module_or_struct_new_call?(node)
171
- def_node_matcher :class_or_module_or_struct_new_call?, <<~PATTERN
172
- ({block numblock} (send (const {nil? cbase} {:Class :Module :Struct}) :new ...) ...)
170
+ # @!method class_constructor?(node)
171
+ def_node_matcher :class_constructor?, <<~PATTERN
172
+ ({block numblock} {
173
+ (send (const {nil? cbase} {:Class :Module :Struct}) :new ...)
174
+ (send (const {nil? cbase} :Data) :define ...)
175
+ } ...)
173
176
  PATTERN
174
177
 
175
178
  def check_node(node)
@@ -270,7 +273,7 @@ module RuboCop
270
273
 
271
274
  def eval_call?(child)
272
275
  class_or_instance_eval?(child) ||
273
- class_or_module_or_struct_new_call?(child) ||
276
+ class_constructor?(child) ||
274
277
  any_context_creating_methods?(child)
275
278
  end
276
279
 
@@ -51,7 +51,7 @@ module RuboCop
51
51
  def on_block(node)
52
52
  return if allowed_method?(node.method_name) || matches_allowed_pattern?(node.method_name)
53
53
  return if method_receiver_excluded?(node)
54
- return if node.class_constructor? || node.struct_constructor?
54
+ return if node.class_constructor?
55
55
 
56
56
  check_code_length(node)
57
57
  end
@@ -3,7 +3,9 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  # This module encapsulates the ability to allow certain methods when
6
- # parsing.
6
+ # parsing. Even if the code is in offense, if it contains methods
7
+ # that are allowed. This module is equivalent to the IgnoredMethods module,
8
+ # which will be deprecated in RuboCop 2.0.
7
9
  module AllowedMethods
8
10
  private
9
11
 
@@ -62,10 +62,12 @@ module RuboCop
62
62
  # Returns the end line of a node, which might be a comment and not part of the AST
63
63
  # End line is considered either the line at which another node starts, or
64
64
  # the line at which the parent node ends.
65
- # rubocop:disable Metrics/AbcSize
65
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
66
66
  def find_end_line(node)
67
- if node.if_type? && node.loc.else
67
+ if node.if_type? && node.else?
68
68
  node.loc.else.line
69
+ elsif node.if_type? && node.ternary?
70
+ node.else_branch.loc.line
69
71
  elsif (next_sibling = node.right_sibling)
70
72
  next_sibling.loc.line
71
73
  elsif (parent = node.parent)
@@ -74,7 +76,7 @@ module RuboCop
74
76
  node.loc.end.line
75
77
  end
76
78
  end
77
- # rubocop:enable Metrics/AbcSize
79
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
78
80
  end
79
81
  end
80
82
  end
@@ -95,16 +95,19 @@ module RuboCop
95
95
  use_modifier_form_without_parenthesized_method_call?(method_dispatch_node)
96
96
  end
97
97
 
98
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
98
99
  def def_node_that_require_parentheses(node)
99
100
  last_pair = node.parent.pairs.last
100
101
  return unless last_pair.key.source == last_pair.value.source
101
- return unless (method_dispatch_node = find_ancestor_method_dispatch_node(node))
102
- return unless without_parentheses_call_expr_follows?(method_dispatch_node)
102
+ return unless (dispatch_node = find_ancestor_method_dispatch_node(node))
103
+ return if node.respond_to?(:parenthesized?) && !node.parenthesized?
104
+ return unless last_expression?(dispatch_node) || method_dispatch_as_argument?(dispatch_node)
103
105
 
104
106
  def_node = node.each_ancestor(:send, :csend, :super, :yield).first
105
107
 
106
108
  DefNode.new(def_node) unless def_node && def_node.arguments.empty?
107
109
  end
110
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
108
111
 
109
112
  def find_ancestor_method_dispatch_node(node)
110
113
  return unless (ancestor = node.parent.parent)
@@ -129,15 +132,16 @@ module RuboCop
129
132
  ancestor.ancestors.any? { |node| node.respond_to?(:modifier_form?) && node.modifier_form? }
130
133
  end
131
134
 
132
- def without_parentheses_call_expr_follows?(ancestor)
133
- return false unless ancestor.respond_to?(:parenthesized?) && !ancestor.parenthesized?
135
+ def last_expression?(ancestor)
136
+ ancestor.right_sibling ||
137
+ ancestor.each_ancestor.find { |node| node.assignment? || node.send_type? }&.right_sibling
138
+ end
134
139
 
135
- right_sibling = ancestor.right_sibling
136
- right_sibling ||= ancestor.each_ancestor.find do |node|
137
- node.assignment? || node.send_type?
138
- end&.right_sibling
140
+ def method_dispatch_as_argument?(method_dispatch_node)
141
+ parent = method_dispatch_node.parent
142
+ return false unless parent
139
143
 
140
- !!right_sibling
144
+ parent.call_type? || parent.super_type? || parent.yield_type?
141
145
  end
142
146
 
143
147
  def breakdown_value_types_of_hash(hash_node)
@@ -107,9 +107,9 @@ module RuboCop
107
107
  end
108
108
  end
109
109
 
110
- def empty_brackets?(left_bracket_token, right_bracket_token)
111
- left_index = processed_source.tokens.index(left_bracket_token)
112
- right_index = processed_source.tokens.index(right_bracket_token)
110
+ def empty_brackets?(left_bracket_token, right_bracket_token, tokens: processed_source.tokens)
111
+ left_index = tokens.index(left_bracket_token)
112
+ right_index = tokens.index(right_bracket_token)
113
113
  right_index && left_index == right_index - 1
114
114
  end
115
115
 
@@ -132,7 +132,7 @@ module RuboCop
132
132
 
133
133
  def avoid_comma(kind, comma_begin_pos, extra_info)
134
134
  range = range_between(comma_begin_pos, comma_begin_pos + 1)
135
- article = /array/.match?(kind) ? 'an' : 'a'
135
+ article = kind.include?('array') ? 'an' : 'a'
136
136
  msg = format(
137
137
  MSG,
138
138
  command: 'Avoid',
@@ -34,7 +34,7 @@ module RuboCop
34
34
 
35
35
  allowed = /#{cop_config['AllowedNames'].join('|')}/
36
36
  name = node.loc.name.source.gsub(allowed, '')
37
- return unless /_/.match?(name)
37
+ return unless name.include?('_')
38
38
 
39
39
  add_offense(node.loc.name)
40
40
  end
@@ -129,7 +129,8 @@ module RuboCop
129
129
  end
130
130
 
131
131
  def offense?(node)
132
- (group_style? && access_modifier_is_inlined?(node)) ||
132
+ (group_style? && access_modifier_is_inlined?(node) &&
133
+ !right_siblings_same_inline_method?(node)) ||
133
134
  (inline_style? && access_modifier_is_not_inlined?(node))
134
135
  end
135
136
 
@@ -149,6 +150,12 @@ module RuboCop
149
150
  !access_modifier_is_inlined?(node)
150
151
  end
151
152
 
153
+ def right_siblings_same_inline_method?(node)
154
+ node.right_siblings.any? do |sibling|
155
+ sibling.send_type? && sibling.method?(node.method_name) && !sibling.arguments.empty?
156
+ end
157
+ end
158
+
152
159
  def message(range)
153
160
  access_modifier = range.source
154
161
 
@@ -84,6 +84,7 @@ module RuboCop
84
84
  def on_def(node)
85
85
  return unless node.body
86
86
  return unless (rest_args_name, args = use_rest_arguments?(node.arguments))
87
+ return if args.any?(&:default?)
87
88
 
88
89
  node.each_descendant(:send) do |send_node|
89
90
  kwargs_name, block_name = extract_argument_names_from(args)
@@ -178,7 +178,7 @@ module RuboCop
178
178
  end
179
179
 
180
180
  def compact_node_name?(node)
181
- /::/.match?(node.identifier.source)
181
+ node.identifier.source.include?('::')
182
182
  end
183
183
  end
184
184
  end
@@ -148,7 +148,7 @@ module RuboCop
148
148
  end
149
149
 
150
150
  def contains_backtick?(node)
151
- /`/.match?(node_body(node))
151
+ node_body(node).include?('`')
152
152
  end
153
153
 
154
154
  def node_body(node)
@@ -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
@@ -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
@@ -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
 
@@ -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)
@@ -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(')')
@@ -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
@@ -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
 
@@ -33,12 +33,14 @@ module RuboCop
33
33
  # "bar" != foo
34
34
  # 42 >= foo
35
35
  # 10 < bar
36
+ # 99 == CONST
36
37
  #
37
38
  # # good
38
39
  # foo == 99
39
40
  # foo == "bar"
40
41
  # foo <= 42
41
42
  # bar > 10
43
+ # CONST == 99
42
44
  # "#{interpolation}" == foo
43
45
  # /#{interpolation}/ == foo
44
46
  #
@@ -92,9 +94,10 @@ module RuboCop
92
94
  def on_send(node)
93
95
  return unless yoda_compatible_condition?(node)
94
96
  return if (equality_only? && non_equality_operator?(node)) ||
95
- file_constant_equal_program_name?(node)
97
+ file_constant_equal_program_name?(node) ||
98
+ valid_yoda?(node)
96
99
 
97
- valid_yoda?(node) || add_offense(node) do |corrector|
100
+ add_offense(node) do |corrector|
98
101
  corrector.replace(actual_code_range(node), corrected_code(node))
99
102
  end
100
103
  end
@@ -119,11 +122,11 @@ module RuboCop
119
122
  lhs = node.receiver
120
123
  rhs = node.first_argument
121
124
 
122
- return true if (lhs.literal? && rhs.literal?) ||
123
- (!lhs.literal? && !rhs.literal?) ||
125
+ return true if (constant_portion?(lhs) && constant_portion?(rhs)) ||
126
+ (!constant_portion?(lhs) && !constant_portion?(rhs)) ||
124
127
  interpolation?(lhs)
125
128
 
126
- enforce_yoda? ? lhs.literal? : rhs.literal?
129
+ enforce_yoda? ? constant_portion?(lhs) : constant_portion?(rhs)
127
130
  end
128
131
 
129
132
  def message(node)
@@ -137,6 +140,10 @@ module RuboCop
137
140
  "#{rhs.source} #{reverse_comparison(node.method_name)} #{lhs.source}"
138
141
  end
139
142
 
143
+ def constant_portion?(node)
144
+ node.literal? || node.const_type?
145
+ end
146
+
140
147
  def actual_code_range(node)
141
148
  range_between(node.loc.expression.begin_pos, node.loc.expression.end_pos)
142
149
  end