rubocop 1.44.1 → 1.45.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) 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/layout/array_alignment.rb +1 -1
  9. data/lib/rubocop/cop/layout/class_structure.rb +2 -16
  10. data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -1
  11. data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
  12. data/lib/rubocop/cop/layout/space_around_operators.rb +1 -1
  13. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +11 -11
  14. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +4 -4
  15. data/lib/rubocop/cop/layout/space_inside_string_interpolation.rb +5 -4
  16. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +4 -1
  17. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +1 -1
  18. data/lib/rubocop/cop/lint/nested_method_definition.rb +8 -5
  19. data/lib/rubocop/cop/lint/useless_access_modifier.rb +7 -4
  20. data/lib/rubocop/cop/metrics/block_length.rb +1 -1
  21. data/lib/rubocop/cop/mixin/allowed_methods.rb +3 -1
  22. data/lib/rubocop/cop/mixin/comments_help.rb +5 -3
  23. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +13 -9
  24. data/lib/rubocop/cop/mixin/surrounding_space.rb +3 -3
  25. data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
  26. data/lib/rubocop/cop/naming/class_and_module_camel_case.rb +1 -1
  27. data/lib/rubocop/cop/style/access_modifier_declarations.rb +8 -1
  28. data/lib/rubocop/cop/style/arguments_forwarding.rb +1 -0
  29. data/lib/rubocop/cop/style/class_and_module_children.rb +1 -1
  30. data/lib/rubocop/cop/style/command_literal.rb +1 -1
  31. data/lib/rubocop/cop/style/documentation.rb +1 -1
  32. data/lib/rubocop/cop/style/documentation_method.rb +6 -0
  33. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +18 -3
  34. data/lib/rubocop/cop/style/numbered_parameters_limit.rb +11 -3
  35. data/lib/rubocop/cop/style/operator_method_call.rb +1 -1
  36. data/lib/rubocop/cop/style/redundant_condition.rb +16 -1
  37. data/lib/rubocop/cop/style/redundant_heredoc_delimiter_quotes.rb +58 -0
  38. data/lib/rubocop/cop/style/symbol_array.rb +1 -1
  39. data/lib/rubocop/cop/style/word_array.rb +1 -1
  40. data/lib/rubocop/cop/style/yoda_condition.rb +12 -5
  41. data/lib/rubocop/cop/style/yoda_expression.rb +11 -2
  42. data/lib/rubocop/cop/team.rb +19 -14
  43. data/lib/rubocop/cop/variable_force/scope.rb +3 -3
  44. data/lib/rubocop/options.rb +22 -1
  45. data/lib/rubocop/runner.rb +40 -4
  46. data/lib/rubocop/server/cache.rb +7 -2
  47. data/lib/rubocop/server/cli.rb +37 -18
  48. data/lib/rubocop/server/client_command/exec.rb +1 -1
  49. data/lib/rubocop/server/client_command/start.rb +6 -1
  50. data/lib/rubocop/server/core.rb +23 -8
  51. data/lib/rubocop/version.rb +1 -1
  52. data/lib/rubocop.rb +1 -0
  53. metadata +8 -27
@@ -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.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
+
171
+ node = argument.children.first
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
@@ -24,12 +24,14 @@ module RuboCop
24
24
  # 1 + x
25
25
  # 10 * y
26
26
  # 1 & z
27
+ # 1 + CONST
27
28
  #
28
29
  # # good
29
30
  # 60 * 24
30
31
  # x + 1
31
32
  # y * 10
32
33
  # z & 1
34
+ # CONST + 1
33
35
  #
34
36
  # # good
35
37
  # 1 | x
@@ -50,8 +52,7 @@ module RuboCop
50
52
 
51
53
  lhs = node.receiver
52
54
  rhs = node.first_argument
53
- return if !lhs.numeric_type? || rhs.numeric_type?
54
-
55
+ return unless yoda_expression_constant?(lhs, rhs)
55
56
  return if offended_ancestor?(node)
56
57
 
57
58
  message = format(MSG, source: rhs.source)
@@ -64,6 +65,14 @@ module RuboCop
64
65
 
65
66
  private
66
67
 
68
+ def yoda_expression_constant?(lhs, rhs)
69
+ constant_portion?(lhs) && !constant_portion?(rhs)
70
+ end
71
+
72
+ def constant_portion?(node)
73
+ node.numeric_type? || node.const_type?
74
+ end
75
+
67
76
  def supported_operators
68
77
  Array(cop_config['SupportedOperators'])
69
78
  end
@@ -77,7 +77,7 @@ module RuboCop
77
77
  end
78
78
 
79
79
  # @return [Commissioner::InvestigationReport]
80
- def investigate(processed_source)
80
+ def investigate(processed_source, offset: 0, original: processed_source)
81
81
  be_ready
82
82
 
83
83
  # The autocorrection process may have to be repeated multiple times
@@ -87,14 +87,15 @@ module RuboCop
87
87
  on_duty = roundup_relevant_cops(processed_source.file_path)
88
88
 
89
89
  autocorrect_cops, other_cops = on_duty.partition(&:autocorrect?)
90
+ report = investigate_partial(autocorrect_cops, processed_source,
91
+ offset: offset, original: original)
90
92
 
91
- report = investigate_partial(autocorrect_cops, processed_source)
92
-
93
- unless autocorrect(processed_source, report)
93
+ unless autocorrect(processed_source, report, offset: offset, original: original)
94
94
  # If we corrected some errors, another round of inspection will be
95
95
  # done, and any other offenses will be caught then, so only need
96
96
  # to check other_cops if no correction was done
97
- report = report.merge(investigate_partial(other_cops, processed_source))
97
+ report = report.merge(investigate_partial(other_cops, processed_source,
98
+ offset: offset, original: original))
98
99
  end
99
100
 
100
101
  process_errors(processed_source.path, report.errors)
@@ -116,12 +117,12 @@ module RuboCop
116
117
 
117
118
  private
118
119
 
119
- def autocorrect(processed_source, report)
120
+ def autocorrect(processed_source, report, original:, offset:)
120
121
  @updated_source_file = false
121
122
  return unless autocorrect?
122
123
  return if report.processed_source.parser_error
123
124
 
124
- new_source = autocorrect_report(report)
125
+ new_source = autocorrect_report(report, original: original, offset: offset)
125
126
 
126
127
  return unless new_source
127
128
 
@@ -149,9 +150,9 @@ module RuboCop
149
150
  end
150
151
 
151
152
  # @return [Commissioner::InvestigationReport]
152
- def investigate_partial(cops, processed_source)
153
+ def investigate_partial(cops, processed_source, offset:, original:)
153
154
  commissioner = Commissioner.new(cops, self.class.forces_for(cops), @options)
154
- commissioner.investigate(processed_source)
155
+ commissioner.investigate(processed_source, offset: offset, original: original)
155
156
  end
156
157
 
157
158
  # @return [Array<cop>]
@@ -175,18 +176,22 @@ module RuboCop
175
176
  cop.class.support_target_rails_version?(cop.target_rails_version)
176
177
  end
177
178
 
178
- def autocorrect_report(report)
179
- corrector = collate_corrections(report)
179
+ def autocorrect_report(report, offset:, original:)
180
+ corrector = collate_corrections(report, offset: offset, original: original)
180
181
 
181
182
  corrector.rewrite unless corrector.empty?
182
183
  end
183
184
 
184
- def collate_corrections(report)
185
- corrector = Corrector.new(report.processed_source)
185
+ def collate_corrections(report, offset:, original:)
186
+ corrector = Corrector.new(original)
186
187
 
187
188
  each_corrector(report) do |to_merge|
188
189
  suppress_clobbering do
189
- corrector.merge!(to_merge)
190
+ if offset.positive?
191
+ corrector.import!(to_merge, offset: offset)
192
+ else
193
+ corrector.merge!(to_merge)
194
+ end
190
195
  end
191
196
  end
192
197