rubocop 1.18.2 → 1.19.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +23 -6
  4. data/lib/rubocop.rb +4 -0
  5. data/lib/rubocop/cli.rb +18 -0
  6. data/lib/rubocop/config_loader.rb +1 -1
  7. data/lib/rubocop/config_loader_resolver.rb +22 -7
  8. data/lib/rubocop/config_validator.rb +18 -5
  9. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -1
  10. data/lib/rubocop/cop/correctors/require_library_corrector.rb +23 -0
  11. data/lib/rubocop/cop/documentation.rb +1 -1
  12. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
  13. data/lib/rubocop/cop/internal_affairs.rb +2 -0
  14. data/lib/rubocop/cop/internal_affairs/inherit_deprecated_cop_class.rb +34 -0
  15. data/lib/rubocop/cop/internal_affairs/undefined_config.rb +71 -0
  16. data/lib/rubocop/cop/layout/class_structure.rb +5 -1
  17. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +9 -0
  18. data/lib/rubocop/cop/layout/end_alignment.rb +10 -2
  19. data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -1
  20. data/lib/rubocop/cop/layout/hash_alignment.rb +22 -18
  21. data/lib/rubocop/cop/layout/heredoc_indentation.rb +0 -7
  22. data/lib/rubocop/cop/layout/indentation_style.rb +2 -2
  23. data/lib/rubocop/cop/layout/leading_comment_space.rb +1 -1
  24. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +36 -22
  25. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +22 -9
  26. data/lib/rubocop/cop/layout/space_around_operators.rb +12 -1
  27. data/lib/rubocop/cop/layout/space_before_comment.rb +1 -1
  28. data/lib/rubocop/cop/layout/space_inside_parens.rb +5 -5
  29. data/lib/rubocop/cop/layout/trailing_whitespace.rb +24 -1
  30. data/lib/rubocop/cop/lint/ambiguous_range.rb +105 -0
  31. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +5 -2
  32. data/lib/rubocop/cop/lint/duplicate_branch.rb +2 -1
  33. data/lib/rubocop/cop/lint/duplicate_methods.rb +8 -5
  34. data/lib/rubocop/cop/lint/shadowed_argument.rb +1 -1
  35. data/lib/rubocop/cop/lint/useless_times.rb +1 -1
  36. data/lib/rubocop/cop/mixin/check_line_breakable.rb +2 -2
  37. data/lib/rubocop/cop/mixin/hash_transform_method.rb +6 -1
  38. data/lib/rubocop/cop/mixin/heredoc.rb +7 -0
  39. data/lib/rubocop/cop/mixin/percent_array.rb +13 -7
  40. data/lib/rubocop/cop/mixin/require_library.rb +59 -0
  41. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  42. data/lib/rubocop/cop/naming/inclusive_language.rb +18 -1
  43. data/lib/rubocop/cop/style/block_delimiters.rb +31 -0
  44. data/lib/rubocop/cop/style/commented_keyword.rb +2 -1
  45. data/lib/rubocop/cop/style/conditional_assignment.rb +19 -5
  46. data/lib/rubocop/cop/style/double_cop_disable_directive.rb +1 -7
  47. data/lib/rubocop/cop/style/double_negation.rb +12 -1
  48. data/lib/rubocop/cop/style/encoding.rb +26 -15
  49. data/lib/rubocop/cop/style/eval_with_location.rb +1 -1
  50. data/lib/rubocop/cop/style/explicit_block_argument.rb +32 -7
  51. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +8 -2
  52. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +11 -0
  53. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  54. data/lib/rubocop/cop/style/hash_transform_keys.rb +0 -3
  55. data/lib/rubocop/cop/style/identical_conditional_branches.rb +30 -5
  56. data/lib/rubocop/cop/style/method_def_parentheses.rb +10 -1
  57. data/lib/rubocop/cop/style/missing_else.rb +7 -0
  58. data/lib/rubocop/cop/style/mutable_constant.rb +6 -8
  59. data/lib/rubocop/cop/style/redundant_begin.rb +25 -0
  60. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +83 -0
  61. data/lib/rubocop/cop/style/redundant_sort.rb +2 -2
  62. data/lib/rubocop/cop/style/semicolon.rb +32 -24
  63. data/lib/rubocop/cop/style/single_line_block_params.rb +3 -1
  64. data/lib/rubocop/cop/style/single_line_methods.rb +25 -15
  65. data/lib/rubocop/cop/style/sole_nested_conditional.rb +4 -0
  66. data/lib/rubocop/cop/style/special_global_vars.rb +21 -0
  67. data/lib/rubocop/cop/style/symbol_array.rb +3 -3
  68. data/lib/rubocop/cop/style/word_array.rb +23 -5
  69. data/lib/rubocop/cop/util.rb +7 -2
  70. data/lib/rubocop/formatter/git_hub_actions_formatter.rb +1 -1
  71. data/lib/rubocop/magic_comment.rb +44 -15
  72. data/lib/rubocop/options.rb +1 -1
  73. data/lib/rubocop/version.rb +1 -1
  74. metadata +15 -9
@@ -289,12 +289,16 @@ module RuboCop
289
289
  (node.first_line - 1).downto(1) do |annotation_line|
290
290
  break unless (comment = processed_source.comment_at_line(annotation_line))
291
291
 
292
- first_comment = comment
292
+ first_comment = comment if whole_line_comment_at_line?(annotation_line)
293
293
  end
294
294
 
295
295
  start_line_position(first_comment || node)
296
296
  end
297
297
 
298
+ def whole_line_comment_at_line?(line)
299
+ /\A\s*#/.match?(processed_source.lines[line - 1])
300
+ end
301
+
298
302
  def start_line_position(node)
299
303
  buffer.line_range(node.loc.line).begin_pos - 1
300
304
  end
@@ -38,12 +38,14 @@ module RuboCop
38
38
  class EmptyLineAfterGuardClause < Base
39
39
  include RangeHelp
40
40
  extend AutoCorrector
41
+ extend Util
41
42
 
42
43
  MSG = 'Add empty line after guard clause.'
43
44
  END_OF_HEREDOC_LINE = 1
44
45
 
45
46
  def on_if(node)
46
47
  return if correct_style?(node)
48
+ return if multiple_statements_on_line?(node)
47
49
 
48
50
  if node.modifier_form? && (heredoc_node = last_heredoc_argument(node))
49
51
  return if next_line_empty_or_enable_directive_comment?(heredoc_line(node, heredoc_node))
@@ -166,6 +168,13 @@ module RuboCop
166
168
  node
167
169
  end
168
170
  end
171
+
172
+ def multiple_statements_on_line?(node)
173
+ parent = node.parent
174
+ return false unless parent
175
+
176
+ parent.begin_type? && parent.single_line?
177
+ end
169
178
  end
170
179
  end
171
180
  end
@@ -165,9 +165,11 @@ module RuboCop
165
165
  end
166
166
 
167
167
  def alignment_node_for_variable_style(node)
168
- return node.parent if node.case_type? && node.argument?
168
+ return node.parent if node.case_type? && node.argument? &&
169
+ node.loc.line == node.parent.loc.line
170
+
171
+ assignment = assignment_or_operator_method(node)
169
172
 
170
- assignment = node.ancestors.find(&:assignment_or_similar?)
171
173
  if assignment && !line_break_before_keyword?(assignment.source_range, node)
172
174
  assignment
173
175
  else
@@ -177,6 +179,12 @@ module RuboCop
177
179
  node
178
180
  end
179
181
  end
182
+
183
+ def assignment_or_operator_method(node)
184
+ node.ancestors.find do |ancestor|
185
+ ancestor.assignment_or_similar? || ancestor.send_type? && ancestor.operator_method?
186
+ end
187
+ end
180
188
  end
181
189
  end
182
190
  end
@@ -154,7 +154,7 @@ module RuboCop
154
154
 
155
155
  def on_send(node)
156
156
  return if style != :consistent && enforce_first_argument_with_fixed_indentation?
157
- return if !node.arguments? || bare_operator?(node)
157
+ return if !node.arguments? || bare_operator?(node) || node.setter_method?
158
158
 
159
159
  indent = base_indentation(node) + configured_indentation_width
160
160
 
@@ -213,18 +213,22 @@ module RuboCop
213
213
  check_pairs(node)
214
214
  end
215
215
 
216
- attr_accessor :offences_by, :column_deltas
216
+ attr_accessor :offenses_by, :column_deltas
217
217
 
218
218
  private
219
219
 
220
220
  def autocorrect_incompatible_with_other_cops?(node)
221
- enforce_first_argument_with_fixed_indentation? &&
222
- node.pairs.any? &&
223
- node.parent&.call_type? && node.parent.loc.selector.line == node.pairs.first.loc.line
221
+ return false unless enforce_first_argument_with_fixed_indentation? &&
222
+ node.pairs.any? &&
223
+ node.parent&.call_type?
224
+
225
+ parent_loc = node.parent.loc
226
+ selector = parent_loc.selector || parent_loc.expression
227
+ selector.line == node.pairs.first.loc.line
224
228
  end
225
229
 
226
230
  def reset!
227
- self.offences_by = {}
231
+ self.offenses_by = {}
228
232
  self.column_deltas = Hash.new { |hash, key| hash[key] = {} }
229
233
  end
230
234
 
@@ -248,33 +252,33 @@ module RuboCop
248
252
  end
249
253
  end
250
254
 
251
- add_offences
255
+ add_offenses
252
256
  end
253
257
 
254
- def add_offences
255
- kwsplat_offences = offences_by.delete(KeywordSplatAlignment)
256
- register_offences_with_format(kwsplat_offences, KeywordSplatAlignment)
258
+ def add_offenses
259
+ kwsplat_offenses = offenses_by.delete(KeywordSplatAlignment)
260
+ register_offenses_with_format(kwsplat_offenses, KeywordSplatAlignment)
257
261
 
258
- format, offences = offences_by.min_by { |_, v| v.length }
259
- register_offences_with_format(offences, format)
262
+ format, offenses = offenses_by.min_by { |_, v| v.length }
263
+ register_offenses_with_format(offenses, format)
260
264
  end
261
265
 
262
- def register_offences_with_format(offences, format)
263
- (offences || []).each do |offence|
264
- add_offense(offence, message: MESSAGES[format]) do |corrector|
265
- delta = column_deltas[alignment_for(offence).first.class][offence]
266
+ def register_offenses_with_format(offenses, format)
267
+ (offenses || []).each do |offense|
268
+ add_offense(offense, message: MESSAGES[format]) do |corrector|
269
+ delta = column_deltas[alignment_for(offense).first.class][offense]
266
270
 
267
- correct_node(corrector, offence, delta) unless delta.nil?
271
+ correct_node(corrector, offense, delta) unless delta.nil?
268
272
  end
269
273
  end
270
274
  end
271
275
 
272
276
  def check_delta(delta, node:, alignment:)
273
- offences_by[alignment.class] ||= []
277
+ offenses_by[alignment.class] ||= []
274
278
  return if good_alignment? delta
275
279
 
276
280
  column_deltas[alignment.class][node] = delta
277
- offences_by[alignment.class].push(node)
281
+ offenses_by[alignment.class].push(node)
278
282
  end
279
283
 
280
284
  def ignore_hash_argument?(node)
@@ -143,13 +143,6 @@ module RuboCop
143
143
  indent_level(base_line)
144
144
  end
145
145
 
146
- def indent_level(str)
147
- indentations = str.lines
148
- .map { |line| line[/^\s*/] }
149
- .reject { |line| line.end_with?("\n") }
150
- indentations.empty? ? 0 : indentations.min_by(&:size).size
151
- end
152
-
153
146
  # Returns '~', '-' or nil
154
147
  def heredoc_indent_type(node)
155
148
  node.source[/^<<([~-])/, 1]
@@ -43,7 +43,7 @@ module RuboCop
43
43
  str_ranges = string_literal_ranges(processed_source.ast)
44
44
 
45
45
  processed_source.lines.each.with_index(1) do |line, lineno|
46
- next unless (range = find_offence(line, lineno))
46
+ next unless (range = find_offense(line, lineno))
47
47
  next if in_string_literal?(str_ranges, range)
48
48
 
49
49
  add_offense(range) { |corrector| autocorrect(corrector, range) }
@@ -60,7 +60,7 @@ module RuboCop
60
60
  end
61
61
  end
62
62
 
63
- def find_offence(line, lineno)
63
+ def find_offense(line, lineno)
64
64
  match = if style == :spaces
65
65
  line.match(/\A\s*\t+/)
66
66
  else
@@ -57,7 +57,7 @@ module RuboCop
57
57
 
58
58
  def on_new_investigation
59
59
  processed_source.comments.each do |comment|
60
- next unless /\A#+[^#\s=:+-]/.match?(comment.text)
60
+ next unless /\A#+[^#\s=+-]/.match?(comment.text)
61
61
  next if comment.loc.line == 1 && allowed_on_first_line?(comment)
62
62
  next if doxygen_comment_style?(comment)
63
63
  next if gemfile_ruby_comment?(comment)
@@ -11,8 +11,7 @@ module RuboCop
11
11
  # concatenated string parts shall be indented regardless of `EnforcedStyle` configuration.
12
12
  #
13
13
  # If `EnforcedStyle: indented` is set, it's the second line that shall be indented one step
14
- # more than the first line. Lines 3 and forward shall be aligned with line 2. Here too there
15
- # are exceptions. Values in a hash literal are always aligned.
14
+ # more than the first line. Lines 3 and forward shall be aligned with line 2.
16
15
  #
17
16
  # @example
18
17
  # # bad
@@ -34,29 +33,44 @@ module RuboCop
34
33
  # 'z'
35
34
  # end
36
35
  #
37
- # my_hash = {
38
- # first: 'a message' \
39
- # 'in two parts'
40
- # }
41
- #
42
36
  # @example EnforcedStyle: aligned (default)
43
37
  # # bad
44
38
  # puts 'x' \
45
39
  # 'y'
46
40
  #
41
+ # my_hash = {
42
+ # first: 'a message' \
43
+ # 'in two parts'
44
+ # }
45
+ #
47
46
  # # good
48
47
  # puts 'x' \
49
48
  # 'y'
50
49
  #
50
+ # my_hash = {
51
+ # first: 'a message' \
52
+ # 'in two parts'
53
+ # }
54
+ #
51
55
  # @example EnforcedStyle: indented
52
56
  # # bad
53
57
  # result = 'x' \
54
58
  # 'y'
55
59
  #
60
+ # my_hash = {
61
+ # first: 'a message' \
62
+ # 'in two parts'
63
+ # }
64
+ #
56
65
  # # good
57
66
  # result = 'x' \
58
67
  # 'y'
59
68
  #
69
+ # my_hash = {
70
+ # first: 'a message' \
71
+ # 'in two parts'
72
+ # }
73
+ #
60
74
  class LineEndStringConcatenationIndentation < Base
61
75
  include ConfigurableEnforcedStyle
62
76
  include Alignment
@@ -70,7 +84,7 @@ module RuboCop
70
84
  return unless strings_concatenated_with_backslash?(node)
71
85
 
72
86
  children = node.children
73
- if style == :aligned && !always_indented?(node) || always_aligned?(node)
87
+ if style == :aligned && !always_indented?(node)
74
88
  check_aligned(children, 1)
75
89
  else
76
90
  check_indented(children)
@@ -85,24 +99,15 @@ module RuboCop
85
99
  private
86
100
 
87
101
  def strings_concatenated_with_backslash?(dstr_node)
88
- !dstr_node.heredoc? &&
89
- !single_string_literal?(dstr_node) &&
90
- dstr_node.children.length > 1 &&
91
- dstr_node.children.all? { |c| c.str_type? || c.dstr_type? }
92
- end
93
-
94
- def single_string_literal?(dstr_node)
95
- dstr_node.loc.respond_to?(:begin) && dstr_node.loc.begin
102
+ dstr_node.multiline? &&
103
+ dstr_node.children.all? { |c| c.str_type? || c.dstr_type? } &&
104
+ dstr_node.children.none?(&:multiline?)
96
105
  end
97
106
 
98
107
  def always_indented?(dstr_node)
99
108
  PARENT_TYPES_FOR_INDENTED.include?(dstr_node.parent&.type)
100
109
  end
101
110
 
102
- def always_aligned?(dstr_node)
103
- dstr_node.parent&.pair_type?
104
- end
105
-
106
111
  def check_aligned(children, start_index)
107
112
  base_column = children[start_index - 1].loc.column
108
113
  children[start_index..-1].each do |child|
@@ -113,11 +118,20 @@ module RuboCop
113
118
  end
114
119
 
115
120
  def check_indented(children)
116
- base_column = children[0].source_range.source_line =~ /\S/
117
- @column_delta = base_column + configured_indentation_width - children[1].loc.column
121
+ @column_delta = base_column(children[0]) + configured_indentation_width -
122
+ children[1].loc.column
118
123
  add_offense_and_correction(children[1], MSG_INDENT) if @column_delta != 0
119
124
  end
120
125
 
126
+ def base_column(child)
127
+ grandparent = child.parent.parent
128
+ if grandparent&.type == :pair
129
+ grandparent.loc.column
130
+ else
131
+ child.source_range.source_line =~ /\S/
132
+ end
133
+ end
134
+
121
135
  def add_offense_and_correction(node, message)
122
136
  add_offense(node, message: message) { |corrector| autocorrect(corrector, node) }
123
137
  end
@@ -29,8 +29,7 @@ module RuboCop
29
29
  MSG = '`%<kw_loc>s` at %<kw_loc_line>d, %<kw_loc_column>d is not ' \
30
30
  'aligned with `%<beginning>s` at ' \
31
31
  '%<begin_loc_line>d, %<begin_loc_column>d.'
32
- ANCESTOR_TYPES = %i[kwbegin def defs class module].freeze
33
- RUBY_2_5_ANCESTOR_TYPES = (ANCESTOR_TYPES + %i[block]).freeze
32
+ ANCESTOR_TYPES = %i[kwbegin def defs class module block].freeze
34
33
  ANCESTOR_TYPES_WITH_ACCESS_MODIFIERS = %i[def defs].freeze
35
34
  ALTERNATIVE_ACCESS_MODIFIERS = %i[public_class_method private_class_method].freeze
36
35
 
@@ -118,6 +117,8 @@ module RuboCop
118
117
  ancestor_node = ancestor_node(node)
119
118
 
120
119
  return ancestor_node if ancestor_node.nil? || ancestor_node.kwbegin_type?
120
+ return if ancestor_node.respond_to?(:send_node) &&
121
+ aligned_with_line_break_method?(ancestor_node, node)
121
122
 
122
123
  assignment_node = assignment_node(ancestor_node)
123
124
  return assignment_node if same_line?(ancestor_node, assignment_node)
@@ -129,14 +130,26 @@ module RuboCop
129
130
  end
130
131
 
131
132
  def ancestor_node(node)
132
- ancestor_types =
133
- if target_ruby_version >= 2.5
134
- RUBY_2_5_ANCESTOR_TYPES
135
- else
136
- ANCESTOR_TYPES
137
- end
133
+ node.each_ancestor(*ANCESTOR_TYPES).first
134
+ end
135
+
136
+ def aligned_with_line_break_method?(ancestor_node, node)
137
+ send_node_loc = ancestor_node.send_node.loc
138
+ do_keyword_line = ancestor_node.loc.begin.line
139
+ rescue_keyword_column = node.loc.keyword.column
140
+ selector = send_node_loc.respond_to?(:selector) ? send_node_loc.selector : send_node_loc
141
+
142
+ if aligned_with_leading_dot?(do_keyword_line, send_node_loc, rescue_keyword_column)
143
+ return true
144
+ end
145
+
146
+ do_keyword_line == selector.line && rescue_keyword_column == selector.column
147
+ end
148
+
149
+ def aligned_with_leading_dot?(do_keyword_line, send_node_loc, rescue_keyword_column)
150
+ return false unless send_node_loc.respond_to?(:dot) && (dot = send_node_loc.dot)
138
151
 
139
- node.each_ancestor(*ancestor_types).first
152
+ do_keyword_line == dot.line && rescue_keyword_column == dot.column
140
153
  end
141
154
 
142
155
  def assignment_node(node)
@@ -63,6 +63,10 @@ module RuboCop
63
63
  [Style::SelfAssignment]
64
64
  end
65
65
 
66
+ def on_sclass(node)
67
+ check_operator(:sclass, node.loc.operator, node.source_range)
68
+ end
69
+
66
70
  def on_pair(node)
67
71
  return unless node.hash_rocket?
68
72
 
@@ -104,6 +108,14 @@ module RuboCop
104
108
  check_operator(:assignment, node.loc.operator, rhs.source_range)
105
109
  end
106
110
 
111
+ def on_casgn(node)
112
+ _, _, right, = *node
113
+
114
+ return unless right
115
+
116
+ check_operator(:assignment, node.loc.operator, right.source_range)
117
+ end
118
+
107
119
  def on_binary(node)
108
120
  _, rhs, = *node
109
121
 
@@ -130,7 +142,6 @@ module RuboCop
130
142
  alias on_and on_binary
131
143
  alias on_lvasgn on_assignment
132
144
  alias on_masgn on_assignment
133
- alias on_casgn on_special_asgn
134
145
  alias on_ivasgn on_assignment
135
146
  alias on_cvasgn on_assignment
136
147
  alias on_gvasgn on_assignment
@@ -18,7 +18,7 @@ module RuboCop
18
18
  MSG = 'Put a space before an end-of-line comment.'
19
19
 
20
20
  def on_new_investigation
21
- processed_source.tokens.each_cons(2) do |token1, token2|
21
+ processed_source.sorted_tokens.each_cons(2) do |token1, token2|
22
22
  next unless token2.comment?
23
23
  next unless token1.line == token2.line
24
24
  next unless token1.pos.end == token2.pos.begin
@@ -43,12 +43,12 @@ module RuboCop
43
43
  MSG_SPACE = 'No space inside parentheses detected.'
44
44
 
45
45
  def on_new_investigation
46
- @processed_source = processed_source
46
+ tokens = processed_source.sorted_tokens
47
47
 
48
48
  if style == :space
49
- process_with_space_style(processed_source)
49
+ process_with_space_style(tokens)
50
50
  else
51
- each_extraneous_space(processed_source.tokens) do |range|
51
+ each_extraneous_space(tokens) do |range|
52
52
  add_offense(range) do |corrector|
53
53
  corrector.remove(range)
54
54
  end
@@ -58,8 +58,8 @@ module RuboCop
58
58
 
59
59
  private
60
60
 
61
- def process_with_space_style(processed_source)
62
- processed_source.tokens.each_cons(2) do |token1, token2|
61
+ def process_with_space_style(tokens)
62
+ tokens.each_cons(2) do |token1, token2|
63
63
  each_extraneous_space_in_empty_parens(token1, token2) do |range|
64
64
  add_offense(range) do |corrector|
65
65
  corrector.remove(range)