rubocop 1.18.3 → 1.20.0

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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +46 -7
  4. data/lib/rubocop/cli.rb +18 -0
  5. data/lib/rubocop/config_loader.rb +2 -2
  6. data/lib/rubocop/config_loader_resolver.rb +21 -6
  7. data/lib/rubocop/config_validator.rb +18 -5
  8. data/lib/rubocop/cop/bundler/gem_filename.rb +103 -0
  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/inherit_deprecated_cop_class.rb +34 -0
  14. data/lib/rubocop/cop/internal_affairs/undefined_config.rb +71 -0
  15. data/lib/rubocop/cop/internal_affairs.rb +2 -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 +33 -14
  25. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +3 -0
  26. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +22 -9
  27. data/lib/rubocop/cop/layout/space_around_operators.rb +8 -1
  28. data/lib/rubocop/cop/layout/space_before_comment.rb +1 -1
  29. data/lib/rubocop/cop/layout/space_inside_parens.rb +5 -5
  30. data/lib/rubocop/cop/layout/trailing_whitespace.rb +24 -1
  31. data/lib/rubocop/cop/lint/ambiguous_range.rb +105 -0
  32. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +5 -2
  33. data/lib/rubocop/cop/lint/debugger.rb +2 -2
  34. data/lib/rubocop/cop/lint/duplicate_branch.rb +2 -1
  35. data/lib/rubocop/cop/lint/duplicate_methods.rb +8 -5
  36. data/lib/rubocop/cop/lint/shadowed_argument.rb +1 -1
  37. data/lib/rubocop/cop/mixin/annotation_comment.rb +57 -34
  38. data/lib/rubocop/cop/mixin/check_line_breakable.rb +2 -2
  39. data/lib/rubocop/cop/mixin/documentation_comment.rb +5 -2
  40. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +14 -1
  41. data/lib/rubocop/cop/mixin/hash_transform_method.rb +6 -1
  42. data/lib/rubocop/cop/mixin/heredoc.rb +7 -0
  43. data/lib/rubocop/cop/mixin/percent_array.rb +13 -7
  44. data/lib/rubocop/cop/mixin/require_library.rb +59 -0
  45. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  46. data/lib/rubocop/cop/naming/inclusive_language.rb +18 -1
  47. data/lib/rubocop/cop/style/block_delimiters.rb +39 -6
  48. data/lib/rubocop/cop/style/comment_annotation.rb +25 -39
  49. data/lib/rubocop/cop/style/commented_keyword.rb +2 -1
  50. data/lib/rubocop/cop/style/conditional_assignment.rb +19 -5
  51. data/lib/rubocop/cop/style/double_cop_disable_directive.rb +1 -7
  52. data/lib/rubocop/cop/style/double_negation.rb +12 -1
  53. data/lib/rubocop/cop/style/encoding.rb +26 -15
  54. data/lib/rubocop/cop/style/eval_with_location.rb +1 -1
  55. data/lib/rubocop/cop/style/explicit_block_argument.rb +32 -7
  56. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +1 -1
  57. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +11 -0
  58. data/lib/rubocop/cop/style/hash_except.rb +4 -3
  59. data/lib/rubocop/cop/style/hash_transform_keys.rb +0 -3
  60. data/lib/rubocop/cop/style/identical_conditional_branches.rb +30 -5
  61. data/lib/rubocop/cop/style/method_def_parentheses.rb +10 -1
  62. data/lib/rubocop/cop/style/missing_else.rb +7 -0
  63. data/lib/rubocop/cop/style/mutable_constant.rb +73 -13
  64. data/lib/rubocop/cop/style/redundant_begin.rb +25 -0
  65. data/lib/rubocop/cop/style/redundant_freeze.rb +4 -3
  66. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +83 -0
  67. data/lib/rubocop/cop/style/redundant_sort.rb +2 -2
  68. data/lib/rubocop/cop/style/semicolon.rb +32 -24
  69. data/lib/rubocop/cop/style/single_line_block_params.rb +3 -1
  70. data/lib/rubocop/cop/style/single_line_methods.rb +14 -9
  71. data/lib/rubocop/cop/style/sole_nested_conditional.rb +4 -0
  72. data/lib/rubocop/cop/style/special_global_vars.rb +21 -0
  73. data/lib/rubocop/cop/style/struct_inheritance.rb +3 -0
  74. data/lib/rubocop/cop/style/symbol_array.rb +3 -3
  75. data/lib/rubocop/cop/style/word_array.rb +23 -5
  76. data/lib/rubocop/cop/util.rb +7 -2
  77. data/lib/rubocop/formatter/git_hub_actions_formatter.rb +1 -1
  78. data/lib/rubocop/magic_comment.rb +44 -15
  79. data/lib/rubocop/options.rb +1 -1
  80. data/lib/rubocop/version.rb +1 -1
  81. data/lib/rubocop.rb +6 -1
  82. metadata +12 -5
@@ -8,7 +8,7 @@ module RuboCop
8
8
 
9
9
  # @api private
10
10
  def department_to_basename(department)
11
- "cops_#{department.downcase}"
11
+ "cops_#{department.to_s.downcase.tr('/', '_')}"
12
12
  end
13
13
 
14
14
  # @api private
@@ -50,7 +50,7 @@ module RuboCop
50
50
  # spec.add_dependency 'rubocop'
51
51
  # # For tests
52
52
  # spec.add_dependency 'rspec'
53
- class OrderedDependencies < Cop
53
+ class OrderedDependencies < Cop # rubocop:disable InternalAffairs/InheritDeprecatedCopClass
54
54
  include ConfigurableEnforcedStyle
55
55
  include OrderedGemNode
56
56
 
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module InternalAffairs
6
+ # `RuboCop::Cop::Cop` is deprecated and will be removed in Rubocop 2.0.
7
+ # Your custom cop class should inherit from `RuboCop::Cop::Base` instead of
8
+ # `RuboCop::Cop::Cop`.
9
+ #
10
+ # See "v1 Upgrade Notes" for more details:
11
+ # https://docs.rubocop.org/rubocop/v1_upgrade_notes.html
12
+ #
13
+ # @example
14
+ # # bad
15
+ # class Foo < Cop
16
+ # end
17
+ #
18
+ # # good
19
+ # class Foo < Base
20
+ # end
21
+ #
22
+ class InheritDeprecatedCopClass < Base
23
+ MSG = 'Use `Base` instead of `Cop`.'
24
+
25
+ def on_class(node)
26
+ return unless (parent_class = node.parent_class)
27
+ return unless parent_class.children.last == :Cop
28
+
29
+ add_offense(parent_class)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module InternalAffairs
6
+ # Looks for references to a cop configuration key that isn't defined in config/default.yml.
7
+ class UndefinedConfig < Base
8
+ ALLOWED_CONFIGURATIONS = %w[
9
+ Safe SafeAutoCorrect AutoCorrect Severity StyleGuide Details Reference Include Exclude
10
+ ].freeze
11
+ RESTRICT_ON_SEND = %i[[] fetch].freeze
12
+ MSG = '`%<name>s` is not defined in the configuration for `%<cop>s` ' \
13
+ 'in `config/default.yml`.'
14
+
15
+ # @!method cop_class_def(node)
16
+ def_node_search :cop_class_def, <<~PATTERN
17
+ (class _ (const _ {:Base :Cop}) ...)
18
+ PATTERN
19
+
20
+ # @!method cop_config_accessor?(node)
21
+ def_node_matcher :cop_config_accessor?, <<~PATTERN
22
+ (send (send nil? :cop_config) {:[] :fetch} ${str sym}...)
23
+ PATTERN
24
+
25
+ def on_new_investigation
26
+ super
27
+ return unless processed_source.ast
28
+
29
+ cop_class = cop_class_def(processed_source.ast).first
30
+ return unless (@cop_class_name = extract_cop_name(cop_class))
31
+
32
+ @config_for_cop = RuboCop::ConfigLoader.default_configuration.for_cop(@cop_class_name)
33
+ end
34
+
35
+ def on_send(node)
36
+ return unless cop_class_name
37
+ return unless (config_name_node = cop_config_accessor?(node))
38
+ return if always_allowed?(config_name_node)
39
+ return if configuration_key_defined?(config_name_node)
40
+
41
+ message = format(MSG, name: config_name_node.value, cop: cop_class_name)
42
+ add_offense(config_name_node, message: message)
43
+ end
44
+
45
+ private
46
+
47
+ attr_reader :config_for_cop, :cop_class_name
48
+
49
+ def extract_cop_name(class_node)
50
+ return unless class_node
51
+
52
+ segments = [class_node].concat(
53
+ class_node.each_ancestor(:class, :module).take_while do |n|
54
+ n.identifier.short_name != :Cop
55
+ end
56
+ )
57
+
58
+ segments.reverse_each.map { |s| s.identifier.short_name }.join('/')
59
+ end
60
+
61
+ def always_allowed?(node)
62
+ ALLOWED_CONFIGURATIONS.include?(node.value)
63
+ end
64
+
65
+ def configuration_key_defined?(node)
66
+ config_for_cop.key?(node.value)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative 'internal_affairs/empty_line_between_expect_offense_and_correction'
4
4
  require_relative 'internal_affairs/example_description'
5
+ require_relative 'internal_affairs/inherit_deprecated_cop_class'
5
6
  require_relative 'internal_affairs/method_name_equal'
6
7
  require_relative 'internal_affairs/node_destructuring'
7
8
  require_relative 'internal_affairs/node_matcher_directive'
@@ -12,4 +13,5 @@ require_relative 'internal_affairs/redundant_let_rubocop_config_new'
12
13
  require_relative 'internal_affairs/redundant_location_argument'
13
14
  require_relative 'internal_affairs/redundant_message_argument'
14
15
  require_relative 'internal_affairs/style_detected_api_use'
16
+ require_relative 'internal_affairs/undefined_config'
15
17
  require_relative 'internal_affairs/useless_message_assertion'
@@ -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)
@@ -94,10 +108,6 @@ module RuboCop
94
108
  PARENT_TYPES_FOR_INDENTED.include?(dstr_node.parent&.type)
95
109
  end
96
110
 
97
- def always_aligned?(dstr_node)
98
- dstr_node.parent&.pair_type?
99
- end
100
-
101
111
  def check_aligned(children, start_index)
102
112
  base_column = children[start_index - 1].loc.column
103
113
  children[start_index..-1].each do |child|
@@ -108,11 +118,20 @@ module RuboCop
108
118
  end
109
119
 
110
120
  def check_indented(children)
111
- base_column = children[0].source_range.source_line =~ /\S/
112
- @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
113
123
  add_offense_and_correction(children[1], MSG_INDENT) if @column_delta != 0
114
124
  end
115
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
+
116
135
  def add_offense_and_correction(node, message)
117
136
  add_offense(node, message: message) { |corrector| autocorrect(corrector, node) }
118
137
  end