rubocop 1.14.0 → 1.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -1
  3. data/config/default.yml +120 -34
  4. data/lib/rubocop.rb +8 -0
  5. data/lib/rubocop/cli/command/suggest_extensions.rb +3 -3
  6. data/lib/rubocop/config_loader.rb +1 -1
  7. data/lib/rubocop/config_validator.rb +5 -5
  8. data/lib/rubocop/cop/base.rb +2 -2
  9. data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -1
  10. data/lib/rubocop/cop/bundler/gem_version.rb +38 -4
  11. data/lib/rubocop/cop/corrector.rb +4 -4
  12. data/lib/rubocop/cop/generator.rb +1 -1
  13. data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
  14. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -1
  15. data/lib/rubocop/cop/layout/argument_alignment.rb +30 -12
  16. data/lib/rubocop/cop/layout/array_alignment.rb +2 -2
  17. data/lib/rubocop/cop/layout/block_alignment.rb +1 -1
  18. data/lib/rubocop/cop/layout/case_indentation.rb +57 -9
  19. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +7 -1
  20. data/lib/rubocop/cop/layout/comment_indentation.rb +1 -1
  21. data/lib/rubocop/cop/layout/dot_position.rb +7 -1
  22. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +13 -15
  23. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +2 -2
  24. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +14 -2
  25. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +1 -1
  26. data/lib/rubocop/cop/layout/hash_alignment.rb +40 -14
  27. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +1 -1
  28. data/lib/rubocop/cop/layout/indentation_width.rb +13 -2
  29. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +122 -0
  30. data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +6 -6
  31. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +2 -2
  32. data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +6 -6
  33. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +6 -6
  34. data/lib/rubocop/cop/layout/multiline_method_definition_brace_layout.rb +6 -6
  35. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +3 -3
  36. data/lib/rubocop/cop/layout/parameter_alignment.rb +2 -2
  37. data/lib/rubocop/cop/layout/redundant_line_break.rb +11 -9
  38. data/lib/rubocop/cop/layout/space_around_keyword.rb +28 -0
  39. data/lib/rubocop/cop/layout/space_around_operators.rb +7 -1
  40. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +4 -0
  41. data/lib/rubocop/cop/lint/empty_block.rb +18 -2
  42. data/lib/rubocop/cop/lint/empty_in_pattern.rb +62 -0
  43. data/lib/rubocop/cop/lint/literal_as_condition.rb +13 -1
  44. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +32 -17
  45. data/lib/rubocop/cop/lint/nested_percent_literal.rb +1 -1
  46. data/lib/rubocop/cop/lint/percent_string_array.rb +1 -1
  47. data/lib/rubocop/cop/lint/percent_symbol_array.rb +1 -1
  48. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +105 -74
  49. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +5 -0
  50. data/lib/rubocop/cop/lint/symbol_conversion.rb +3 -13
  51. data/lib/rubocop/cop/lint/unused_block_argument.rb +1 -1
  52. data/lib/rubocop/cop/lint/useless_assignment.rb +1 -1
  53. data/lib/rubocop/cop/lint/void.rb +1 -1
  54. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  55. data/lib/rubocop/cop/migration/department_name.rb +3 -1
  56. data/lib/rubocop/cop/mixin/check_line_breakable.rb +28 -3
  57. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +6 -0
  58. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +14 -3
  59. data/lib/rubocop/cop/mixin/string_literals_help.rb +2 -4
  60. data/lib/rubocop/cop/mixin/symbol_help.rb +13 -0
  61. data/lib/rubocop/cop/naming/inclusive_language.rb +249 -0
  62. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +2 -2
  63. data/lib/rubocop/cop/style/class_and_module_children.rb +28 -2
  64. data/lib/rubocop/cop/style/empty_literal.rb +8 -1
  65. data/lib/rubocop/cop/style/hash_each_methods.rb +18 -1
  66. data/lib/rubocop/cop/style/identical_conditional_branches.rb +58 -8
  67. data/lib/rubocop/cop/style/in_pattern_then.rb +56 -0
  68. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -1
  69. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +62 -0
  70. data/lib/rubocop/cop/style/multiline_when_then.rb +2 -11
  71. data/lib/rubocop/cop/style/multiple_comparison.rb +1 -1
  72. data/lib/rubocop/cop/style/nil_lambda.rb +29 -12
  73. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +1 -1
  74. data/lib/rubocop/cop/style/quoted_symbols.rb +110 -0
  75. data/lib/rubocop/cop/style/raise_args.rb +2 -0
  76. data/lib/rubocop/cop/style/redundant_begin.rb +1 -1
  77. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +1 -1
  78. data/lib/rubocop/cop/style/redundant_self.rb +24 -2
  79. data/lib/rubocop/cop/style/regexp_literal.rb +10 -1
  80. data/lib/rubocop/cop/style/special_global_vars.rb +3 -3
  81. data/lib/rubocop/cop/style/string_concatenation.rb +32 -5
  82. data/lib/rubocop/cop/style/string_literals.rb +3 -2
  83. data/lib/rubocop/cop/style/string_literals_in_interpolation.rb +1 -0
  84. data/lib/rubocop/cop/style/swap_values.rb +1 -1
  85. data/lib/rubocop/cop/style/top_level_method_definition.rb +83 -0
  86. data/lib/rubocop/cop/style/trivial_accessors.rb +65 -0
  87. data/lib/rubocop/cop/style/unpack_first.rb +1 -1
  88. data/lib/rubocop/cop/style/when_then.rb +6 -2
  89. data/lib/rubocop/cop/variable_force/variable_table.rb +1 -1
  90. data/lib/rubocop/directive_comment.rb +58 -6
  91. data/lib/rubocop/formatter/junit_formatter.rb +21 -6
  92. data/lib/rubocop/options.rb +18 -24
  93. data/lib/rubocop/rake_task.rb +1 -1
  94. data/lib/rubocop/remote_config.rb +10 -2
  95. data/lib/rubocop/rspec/cop_helper.rb +1 -1
  96. data/lib/rubocop/rspec/expect_offense.rb +1 -1
  97. data/lib/rubocop/version.rb +1 -1
  98. metadata +13 -5
@@ -33,7 +33,7 @@ module RuboCop
33
33
  include PercentLiteral
34
34
 
35
35
  MSG = 'Within percent literals, nested percent literals do not ' \
36
- 'function and may be unwanted in the result.'
36
+ 'function and may be unwanted in the result.'
37
37
 
38
38
  # The array of regular expressions representing percent literals that,
39
39
  # if found within a percent literal expression, will cause a
@@ -29,7 +29,7 @@ module RuboCop
29
29
  TRAILING_QUOTE = /['"]?,?$/.freeze
30
30
 
31
31
  MSG = "Within `%w`/`%W`, quotes and ',' are unnecessary and may be " \
32
- 'unwanted in the resulting strings.'
32
+ 'unwanted in the resulting strings.'
33
33
 
34
34
  def on_array(node)
35
35
  process(node, '%w', '%W')
@@ -25,7 +25,7 @@ module RuboCop
25
25
  extend AutoCorrector
26
26
 
27
27
  MSG = "Within `%i`/`%I`, ':' and ',' are unnecessary and may be " \
28
- 'unwanted in the resulting symbols.'
28
+ 'unwanted in the resulting symbols.'
29
29
 
30
30
  def on_array(node)
31
31
  process(node, '%i', '%I')
@@ -25,11 +25,12 @@ module RuboCop
25
25
  #
26
26
  # # good
27
27
  # x += 1
28
- class RedundantCopDisableDirective < Base
28
+ class RedundantCopDisableDirective < Base # rubocop:todo Metrics/ClassLength
29
29
  include RangeHelp
30
30
  extend AutoCorrector
31
31
 
32
32
  COP_NAME = 'Lint/RedundantCopDisableDirective'
33
+ DEPARTMENT_MARKER = 'DEPARTMENT'
33
34
 
34
35
  attr_accessor :offenses_to_check
35
36
 
@@ -41,12 +42,9 @@ module RuboCop
41
42
  def on_new_investigation
42
43
  return unless offenses_to_check
43
44
 
44
- cop_disabled_line_ranges = processed_source.disabled_line_ranges
45
-
46
45
  redundant_cops = Hash.new { |h, k| h[k] = Set.new }
47
46
 
48
- each_redundant_disable(cop_disabled_line_ranges,
49
- offenses_to_check) do |comment, redundant_cop|
47
+ each_redundant_disable do |comment, redundant_cop|
50
48
  redundant_cops[comment].add(redundant_cop)
51
49
  end
52
50
 
@@ -56,20 +54,31 @@ module RuboCop
56
54
 
57
55
  private
58
56
 
57
+ def cop_disabled_line_ranges
58
+ processed_source.disabled_line_ranges
59
+ end
60
+
61
+ def disabled_ranges
62
+ cop_disabled_line_ranges[COP_NAME] || [0..0]
63
+ end
64
+
59
65
  def previous_line_blank?(range)
60
66
  processed_source.buffer.source_line(range.line - 1).blank?
61
67
  end
62
68
 
63
- def comment_range_with_surrounding_space(range)
64
- if previous_line_blank?(range) &&
65
- processed_source.comment_config.comment_only_line?(range.line)
69
+ def comment_range_with_surrounding_space(directive_comment_range, line_comment_range)
70
+ if previous_line_blank?(directive_comment_range) &&
71
+ processed_source.comment_config.comment_only_line?(directive_comment_range.line) &&
72
+ directive_comment_range.begin_pos == line_comment_range.begin_pos
66
73
  # When the previous line is blank, it should be retained
67
- range_with_surrounding_space(range: range, side: :right)
74
+ range_with_surrounding_space(range: directive_comment_range, side: :right)
68
75
  else
69
76
  # Eat the entire comment, the preceding space, and the preceding
70
77
  # newline if there is one.
71
- original_begin = range.begin_pos
72
- range = range_with_surrounding_space(range: range, side: :left, newlines: true)
78
+ original_begin = directive_comment_range.begin_pos
79
+ range = range_with_surrounding_space(
80
+ range: directive_comment_range, side: :left, newlines: true
81
+ )
73
82
 
74
83
  range_with_surrounding_space(range: range,
75
84
  side: :right,
@@ -94,32 +103,34 @@ module RuboCop
94
103
  range_with_surrounding_space(range: range, side: :right, newlines: false)
95
104
  end
96
105
 
97
- def each_redundant_disable(cop_disabled_line_ranges, offenses,
98
- &block)
99
- disabled_ranges = cop_disabled_line_ranges[COP_NAME] || [0..0]
100
-
106
+ def each_redundant_disable(&block)
101
107
  cop_disabled_line_ranges.each do |cop, line_ranges|
102
- each_already_disabled(line_ranges, disabled_ranges) { |comment| yield comment, cop }
103
-
104
- each_line_range(line_ranges, disabled_ranges, offenses, cop, &block)
108
+ each_already_disabled(cop, line_ranges, &block)
109
+ each_line_range(cop, line_ranges, &block)
105
110
  end
106
111
  end
107
112
 
108
- def each_line_range(line_ranges, disabled_ranges, offenses,
109
- cop)
110
- line_ranges.each_with_index do |line_range, ix|
111
- comment = processed_source.comment_at_line(line_range.begin)
112
- next if ignore_offense?(disabled_ranges, line_range)
113
+ def each_line_range(cop, line_ranges)
114
+ line_ranges.each_with_index do |line_range, line_range_index|
115
+ next if ignore_offense?(line_range)
113
116
 
114
- redundant_cop = find_redundant(comment, offenses, cop, line_range, line_ranges[ix + 1])
115
- yield comment, redundant_cop if redundant_cop
117
+ comment = processed_source.comment_at_line(line_range.begin)
118
+ redundant = if all_disabled?(comment)
119
+ find_redundant_all(line_range, line_ranges[line_range_index + 1])
120
+ elsif department_disabled?(cop, comment)
121
+ find_redundant_department(cop, line_range)
122
+ else
123
+ find_redundant_cop(cop, line_range)
124
+ end
125
+
126
+ yield comment, redundant if redundant
116
127
  end
117
128
  end
118
129
 
119
- def each_already_disabled(line_ranges, disabled_ranges)
130
+ def each_already_disabled(cop, line_ranges)
120
131
  line_ranges.each_cons(2) do |previous_range, range|
121
- next if ignore_offense?(disabled_ranges, range)
122
- next if previous_range.end != range.begin
132
+ next if ignore_offense?(range)
133
+ next unless followed_ranges?(previous_range, range)
123
134
 
124
135
  # If a cop is disabled in a range that begins on the same line as
125
136
  # the end of the previous range, it means that the cop was
@@ -130,42 +141,56 @@ module RuboCop
130
141
  # Comments disabling all cops don't count since it's reasonable
131
142
  # to disable a few select cops first and then all cops further
132
143
  # down in the code.
133
- yield comment if comment && !all_disabled?(comment)
144
+ yield comment, cop if comment && !all_disabled?(comment)
134
145
  end
135
146
  end
136
147
 
137
- # rubocop:todo Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
138
- def find_redundant(comment, offenses, cop, line_range, next_line_range)
139
- if all_disabled?(comment)
140
- # If there's a disable all comment followed by a comment
141
- # specifically disabling `cop`, we don't report the `all`
142
- # comment. If the disable all comment is truly redundant, we will
143
- # detect that when examining the comments of another cop, and we
144
- # get the full line range for the disable all.
145
- if (next_line_range.nil? || line_range.last != next_line_range.first) &&
146
- offenses.none? { |o| line_range.cover?(o.line) }
147
- 'all'
148
- end
149
- else
150
- cop_offenses = offenses.select { |o| o.cop_name == cop }
151
- cop if cop_offenses.none? { |o| line_range.cover?(o.line) }
152
- end
148
+ def find_redundant_cop(cop, range)
149
+ cop_offenses = offenses_to_check.select { |offense| offense.cop_name == cop }
150
+ cop if range_with_offense?(range, cop_offenses)
151
+ end
152
+
153
+ def find_redundant_all(range, next_range)
154
+ # If there's a disable all comment followed by a comment
155
+ # specifically disabling `cop`, we don't report the `all`
156
+ # comment. If the disable all comment is truly redundant, we will
157
+ # detect that when examining the comments of another cop, and we
158
+ # get the full line range for the disable all.
159
+ has_no_next_range = next_range.nil? || !followed_ranges?(range, next_range)
160
+ 'all' if has_no_next_range && range_with_offense?(range)
161
+ end
162
+
163
+ def find_redundant_department(cop, range)
164
+ department = cop.split('/').first
165
+ offenses = offenses_to_check.select { |offense| offense.cop_name.start_with?(department) }
166
+ add_department_marker(department) if range_with_offense?(range, offenses)
167
+ end
168
+
169
+ def followed_ranges?(range, next_range)
170
+ range.end == next_range.begin
171
+ end
172
+
173
+ def range_with_offense?(range, offenses = offenses_to_check)
174
+ offenses.none? { |offense| range.cover?(offense.line) }
153
175
  end
154
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
155
176
 
156
177
  def all_disabled?(comment)
157
- /rubocop\s*:\s*(?:disable|todo)\s+all\b/.match?(comment.text)
178
+ DirectiveComment.new(comment).disabled_all?
158
179
  end
159
180
 
160
- def ignore_offense?(disabled_ranges, line_range)
181
+ def ignore_offense?(line_range)
161
182
  disabled_ranges.any? do |range|
162
183
  range.cover?(line_range.min) && range.cover?(line_range.max)
163
184
  end
164
185
  end
165
186
 
187
+ def department_disabled?(cop, comment)
188
+ directive = DirectiveComment.new(comment)
189
+ directive.in_directive_department?(cop) && !directive.overridden_by_department?(cop)
190
+ end
191
+
166
192
  def directive_count(comment)
167
- _, cops_string = DirectiveComment.new(comment).match_captures
168
- cops_string.split(/,\s*/).size
193
+ DirectiveComment.new(comment).directive_count
169
194
  end
170
195
 
171
196
  def add_offenses(redundant_cops)
@@ -179,14 +204,11 @@ module RuboCop
179
204
  end
180
205
 
181
206
  def add_offense_for_entire_comment(comment, cops)
182
- location = comment.loc.expression
183
- cop_list = cops.sort.map { |c| describe(c) }
184
-
185
- add_offense(
186
- location,
187
- message: "Unnecessary disabling of #{cop_list.join(', ')}."
188
- ) do |corrector|
189
- range = comment_range_with_surrounding_space(location)
207
+ location = DirectiveComment.new(comment).range
208
+ cop_names = cops.sort.map { |c| describe(c) }.join(', ')
209
+
210
+ add_offense(location, message: message(cop_names)) do |corrector|
211
+ range = comment_range_with_surrounding_space(location, comment.loc.expression)
190
212
  corrector.remove(range)
191
213
  end
192
214
  end
@@ -197,10 +219,8 @@ module RuboCop
197
219
  ranges = cop_ranges.map { |_, r| r }
198
220
 
199
221
  cop_ranges.each do |cop, range|
200
- add_offense(
201
- range,
202
- message: "Unnecessary disabling of #{describe(cop)}."
203
- ) do |corrector|
222
+ cop_name = describe(cop)
223
+ add_offense(range, message: message(cop_name)) do |corrector|
204
224
  range = directive_range_in_list(range, ranges)
205
225
  corrector.remove(range)
206
226
  end
@@ -208,6 +228,7 @@ module RuboCop
208
228
  end
209
229
 
210
230
  def cop_range(comment, cop)
231
+ cop = remove_department_marker(cop)
211
232
  matching_range(comment.loc.expression, cop) ||
212
233
  matching_range(comment.loc.expression, Badge.parse(cop).cop_name) ||
213
234
  raise("Couldn't find #{cop} in comment: #{comment.text}")
@@ -230,18 +251,16 @@ module RuboCop
230
251
  end
231
252
 
232
253
  def describe(cop)
233
- if cop == 'all'
234
- 'all cops'
235
- elsif all_cop_names.include?(cop)
236
- "`#{cop}`"
237
- else
238
- similar = NameSimilarity.find_similar_name(cop, all_cop_names)
239
- if similar
240
- "`#{cop}` (did you mean `#{similar}`?)"
241
- else
242
- "`#{cop}` (unknown cop)"
243
- end
244
- end
254
+ return 'all cops' if cop == 'all'
255
+ return "`#{remove_department_marker(cop)}` department" if department_marker?(cop)
256
+ return "`#{cop}`" if all_cop_names.include?(cop)
257
+
258
+ similar = NameSimilarity.find_similar_name(cop, all_cop_names)
259
+ similar ? "`#{cop}` (did you mean `#{similar}`?)" : "`#{cop}` (unknown cop)"
260
+ end
261
+
262
+ def message(cop_names)
263
+ "Unnecessary disabling of #{cop_names}."
245
264
  end
246
265
 
247
266
  def all_cop_names
@@ -252,6 +271,18 @@ module RuboCop
252
271
  line = range.source_buffer.source_line(range.last_line)
253
272
  (line =~ /\s*\z/) == range.last_column
254
273
  end
274
+
275
+ def department_marker?(department)
276
+ department.start_with?(DEPARTMENT_MARKER)
277
+ end
278
+
279
+ def remove_department_marker(department)
280
+ department.gsub(DEPARTMENT_MARKER, '')
281
+ end
282
+
283
+ def add_department_marker(department)
284
+ DEPARTMENT_MARKER + department
285
+ end
255
286
  end
256
287
  end
257
288
  end
@@ -54,6 +54,7 @@ module RuboCop
54
54
  directive = DirectiveComment.new(comment)
55
55
 
56
56
  cop_names.each do |name|
57
+ name = name.split('/').first if department?(directive, name)
57
58
  add_offense(
58
59
  range_of_offense(comment, name),
59
60
  message: format(MSG, cop: all_or_name(name))
@@ -119,6 +120,10 @@ module RuboCop
119
120
  def all_or_name(name)
120
121
  name == 'all' ? 'all cops' : name
121
122
  end
123
+
124
+ def department?(directive, name)
125
+ directive.in_directive_department?(name) && !directive.overridden_by_department?(name)
126
+ end
122
127
  end
123
128
  end
124
129
  end
@@ -66,10 +66,11 @@ module RuboCop
66
66
  class SymbolConversion < Base
67
67
  extend AutoCorrector
68
68
  include ConfigurableEnforcedStyle
69
+ include SymbolHelp
69
70
 
70
71
  MSG = 'Unnecessary symbol conversion; use `%<correction>s` instead.'
71
72
  MSG_CONSISTENCY = 'Symbol hash key should be quoted for consistency; ' \
72
- 'use `%<correction>s` instead.'
73
+ 'use `%<correction>s` instead.'
73
74
  RESTRICT_ON_SEND = %i[to_sym intern].freeze
74
75
 
75
76
  def on_send(node)
@@ -138,10 +139,6 @@ module RuboCop
138
139
  node.parent&.array_type? && node.parent&.percent_literal?
139
140
  end
140
141
 
141
- def hash_key?(node)
142
- node.parent&.pair_type? && node == node.parent.child_nodes.first
143
- end
144
-
145
142
  def correct_hash_key(node)
146
143
  # Although some operators can be converted to symbols normally
147
144
  # (ie. `:==`), these are not accepted as hash keys and will
@@ -167,7 +164,7 @@ module RuboCop
167
164
  next if requires_quotes?(key)
168
165
  next if properly_quoted?(key.source, %("#{key.value}"))
169
166
 
170
- correction = "#{quote_type}#{key.value}#{quote_type}"
167
+ correction = %("#{key.value}")
171
168
  register_offense(
172
169
  key,
173
170
  correction: correction,
@@ -175,13 +172,6 @@ module RuboCop
175
172
  )
176
173
  end
177
174
  end
178
-
179
- def quote_type
180
- # Use the `Style/StringLiterals` configuration for quoting symbols
181
- return '"' unless config.for_cop('Style/StringLiterals')['Enabled']
182
-
183
- config.for_cop('Style/StringLiterals')['EnforcedStyle'] == 'single_quotes' ? "'" : '"'
184
- end
185
175
  end
186
176
  end
187
177
  end
@@ -143,7 +143,7 @@ module RuboCop
143
143
 
144
144
  def message_for_underscore_prefix(variable)
145
145
  "If it's necessary, use `_` or `_#{variable.name}` " \
146
- "as an argument name to indicate that it won't be used."
146
+ "as an argument name to indicate that it won't be used."
147
147
  end
148
148
 
149
149
  def define_method_call?(variable)
@@ -85,7 +85,7 @@ module RuboCop
85
85
  return unless assignment.meta_assignment_node.equal?(return_value_node)
86
86
 
87
87
  " Use `#{assignment.operator.sub(/=$/, '')}` " \
88
- "instead of `#{assignment.operator}`."
88
+ "instead of `#{assignment.operator}`."
89
89
  end
90
90
 
91
91
  def similar_name_message(variable)
@@ -104,7 +104,7 @@ module RuboCop
104
104
  end
105
105
 
106
106
  def check_literal(node)
107
- return if !node.literal? || node.xstr_type?
107
+ return if !node.literal? || node.xstr_type? || node.range_type?
108
108
 
109
109
  add_offense(node, message: format(LIT_MSG, lit: node.source))
110
110
  end
@@ -50,7 +50,7 @@ module RuboCop
50
50
  ->(node) { heredoc_node?(node) }
51
51
  else
52
52
  raise ArgumentError, "Unknown foldable type: #{type.inspect}. "\
53
- "Valid foldable types are: #{FOLDABLE_TYPES.join(', ')}."
53
+ "Valid foldable types are: #{FOLDABLE_TYPES.join(', ')}."
54
54
  end
55
55
  end
56
56
  end
@@ -61,7 +61,9 @@ module RuboCop
61
61
  end
62
62
 
63
63
  def valid_content_token?(content_token)
64
- /\W+/.match?(content_token) || DISABLING_COPS_CONTENT_TOKEN.match?(content_token)
64
+ /\W+/.match?(content_token) ||
65
+ DISABLING_COPS_CONTENT_TOKEN.match?(content_token) ||
66
+ Registry.global.department?(content_token)
65
67
  end
66
68
 
67
69
  def contain_unexpected_character_for_department_name?(name)
@@ -70,17 +70,42 @@ module RuboCop
70
70
  def extract_first_element_over_column_limit(node, elements, max)
71
71
  line = node.first_line
72
72
 
73
- # If the first argument is a hash pair but the method is not parenthesized,
74
- # the argument cannot be moved to another line because it cause a syntax error.
75
- elements.shift if node.send_type? && !node.parenthesized? && elements.first.pair_type?
73
+ # If a `send` node is not parenthesized, don't move the first element, because it
74
+ # can result in changed behavior or a syntax error.
75
+ if node.send_type? && !node.parenthesized? && !first_argument_is_heredoc?(node)
76
+ elements = elements.drop(1)
77
+ end
76
78
 
77
79
  i = 0
78
80
  i += 1 while within_column_limit?(elements[i], max, line)
81
+ i = shift_elements_for_heredoc_arg(node, elements, i)
82
+
83
+ return if i.nil?
79
84
  return elements.first if i.zero?
80
85
 
81
86
  elements[i - 1]
82
87
  end
83
88
 
89
+ # @api private
90
+ def first_argument_is_heredoc?(node)
91
+ first_argument = node.first_argument
92
+
93
+ first_argument.respond_to?(:heredoc?) && first_argument.heredoc?
94
+ end
95
+
96
+ # @api private
97
+ # If a send node contains a heredoc argument, splitting cannot happen
98
+ # after the heredoc or else it will cause a syntax error.
99
+ def shift_elements_for_heredoc_arg(node, elements, index)
100
+ return index unless node.send_type?
101
+
102
+ heredoc_index = elements.index { |arg| (arg.str_type? || arg.dstr_type?) && arg.heredoc? }
103
+ return index unless heredoc_index
104
+ return nil if heredoc_index.zero?
105
+
106
+ heredoc_index >= index ? index : heredoc_index + 1
107
+ end
108
+
84
109
  # @api private
85
110
  def within_column_limit?(element, max, line)
86
111
  element && element.loc.column <= max && element.loc.line == line