rubocop 1.85.1 → 1.86.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 (89) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +9 -12
  3. data/lib/rubocop/cache_config.rb +1 -1
  4. data/lib/rubocop/cli/command/auto_generate_config.rb +1 -1
  5. data/lib/rubocop/cli/command/show_cops.rb +2 -2
  6. data/lib/rubocop/cli/command/show_docs_url.rb +1 -1
  7. data/lib/rubocop/config.rb +14 -10
  8. data/lib/rubocop/config_finder.rb +1 -1
  9. data/lib/rubocop/config_loader_resolver.rb +2 -1
  10. data/lib/rubocop/config_store.rb +1 -1
  11. data/lib/rubocop/config_validator.rb +1 -1
  12. data/lib/rubocop/cop/documentation.rb +2 -3
  13. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
  14. data/lib/rubocop/cop/layout/dot_position.rb +1 -1
  15. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +9 -2
  16. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +1 -1
  17. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -0
  18. data/lib/rubocop/cop/layout/end_alignment.rb +6 -3
  19. data/lib/rubocop/cop/layout/line_length.rb +5 -3
  20. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +28 -3
  21. data/lib/rubocop/cop/layout/space_around_keyword.rb +3 -1
  22. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +1 -0
  23. data/lib/rubocop/cop/lint/constant_reassignment.rb +59 -9
  24. data/lib/rubocop/cop/lint/duplicate_methods.rb +55 -8
  25. data/lib/rubocop/cop/lint/empty_conditional_body.rb +6 -1
  26. data/lib/rubocop/cop/lint/empty_in_pattern.rb +8 -1
  27. data/lib/rubocop/cop/lint/empty_when.rb +8 -1
  28. data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
  29. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +16 -0
  30. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +17 -0
  31. data/lib/rubocop/cop/lint/syntax.rb +25 -1
  32. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -0
  33. data/lib/rubocop/cop/lint/unused_method_argument.rb +10 -0
  34. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +4 -4
  35. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +2 -0
  36. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +22 -7
  37. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +1 -1
  38. data/lib/rubocop/cop/registry.rb +20 -13
  39. data/lib/rubocop/cop/style/access_modifier_declarations.rb +14 -2
  40. data/lib/rubocop/cop/style/and_or.rb +1 -0
  41. data/lib/rubocop/cop/style/arguments_forwarding.rb +25 -7
  42. data/lib/rubocop/cop/style/ascii_comments.rb +1 -1
  43. data/lib/rubocop/cop/style/block_delimiters.rb +23 -31
  44. data/lib/rubocop/cop/style/collection_compact.rb +36 -16
  45. data/lib/rubocop/cop/style/concat_array_literals.rb +2 -0
  46. data/lib/rubocop/cop/style/conditional_assignment.rb +0 -4
  47. data/lib/rubocop/cop/style/empty_class_definition.rb +24 -2
  48. data/lib/rubocop/cop/style/file_open.rb +1 -1
  49. data/lib/rubocop/cop/style/global_vars.rb +1 -1
  50. data/lib/rubocop/cop/style/guard_clause.rb +9 -6
  51. data/lib/rubocop/cop/style/hash_lookup_method.rb +7 -0
  52. data/lib/rubocop/cop/style/if_inside_else.rb +1 -5
  53. data/lib/rubocop/cop/style/if_unless_modifier.rb +11 -0
  54. data/lib/rubocop/cop/style/if_with_semicolon.rb +7 -5
  55. data/lib/rubocop/cop/style/ip_addresses.rb +1 -2
  56. data/lib/rubocop/cop/style/magic_comment_format.rb +2 -2
  57. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +5 -3
  58. data/lib/rubocop/cop/style/module_member_existence_check.rb +1 -11
  59. data/lib/rubocop/cop/style/mutable_constant.rb +1 -1
  60. data/lib/rubocop/cop/style/non_nil_check.rb +5 -11
  61. data/lib/rubocop/cop/style/numeric_literals.rb +1 -1
  62. data/lib/rubocop/cop/style/one_class_per_file.rb +24 -4
  63. data/lib/rubocop/cop/style/raise_args.rb +1 -1
  64. data/lib/rubocop/cop/style/redundant_each.rb +3 -3
  65. data/lib/rubocop/cop/style/redundant_line_continuation.rb +16 -0
  66. data/lib/rubocop/cop/style/redundant_parentheses.rb +22 -20
  67. data/lib/rubocop/cop/style/redundant_percent_q.rb +4 -1
  68. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +0 -5
  69. data/lib/rubocop/cop/style/redundant_struct_keyword_init.rb +10 -0
  70. data/lib/rubocop/cop/style/safe_navigation.rb +7 -7
  71. data/lib/rubocop/cop/style/single_line_block_params.rb +1 -1
  72. data/lib/rubocop/cop/style/symbol_proc.rb +4 -3
  73. data/lib/rubocop/cop/style/trailing_method_end_statement.rb +1 -0
  74. data/lib/rubocop/cop/style/yoda_expression.rb +1 -1
  75. data/lib/rubocop/formatter/disabled_config_formatter.rb +1 -1
  76. data/lib/rubocop/formatter/junit_formatter.rb +1 -1
  77. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
  78. data/lib/rubocop/lsp/routes.rb +10 -3
  79. data/lib/rubocop/mcp/server.rb +27 -1
  80. data/lib/rubocop/path_util.rb +14 -2
  81. data/lib/rubocop/plugin/loader.rb +1 -1
  82. data/lib/rubocop/result_cache.rb +10 -1
  83. data/lib/rubocop/rspec/cop_helper.rb +8 -0
  84. data/lib/rubocop/rspec/shared_contexts.rb +11 -2
  85. data/lib/rubocop/runner.rb +8 -3
  86. data/lib/rubocop/server/core.rb +2 -0
  87. data/lib/rubocop/target_finder.rb +1 -1
  88. data/lib/rubocop/version.rb +2 -2
  89. metadata +5 -19
@@ -197,6 +197,13 @@ module RuboCop
197
197
  # @!method sym_name(node)
198
198
  def_node_matcher :sym_name, '(sym $_name)'
199
199
 
200
+ # @!method class_or_module_new_block?(node)
201
+ def_node_matcher :class_or_module_new_block?, <<~PATTERN
202
+ (block
203
+ (send (const _ {:Class :Module}) :new ...)
204
+ ...)
205
+ PATTERN
206
+
200
207
  def on_send(node) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
201
208
  name, original_name = alias_method?(node)
202
209
 
@@ -233,9 +240,12 @@ module RuboCop
233
240
 
234
241
  def check_self_receiver(node, name)
235
242
  enclosing = node.parent_module_name
236
- return unless enclosing
237
-
238
- found_method(node, "#{enclosing}.#{name}")
243
+ if enclosing
244
+ found_method(node, "#{enclosing}.#{name}")
245
+ elsif (anon_block = anonymous_class_block(node))
246
+ scope = qualified_name(anon_block.parent_module_name, nil, 'Object')
247
+ found_method(node, "#{scope}.#{name}", scope_id: anon_block_scope_id(anon_block))
248
+ end
239
249
  end
240
250
 
241
251
  def inside_condition?(node)
@@ -274,16 +284,50 @@ module RuboCop
274
284
  end
275
285
 
276
286
  def found_instance_method(node, name)
277
- return found_sclass_method(node, name) unless (scope = node.parent_module_name)
287
+ if (scope = node.parent_module_name)
288
+ found_method(node, "#{humanize_scope(scope)}#{name}")
289
+ elsif (anon_block = anonymous_class_block(node))
290
+ base = qualified_name(anon_block.parent_module_name, nil, 'Object')
291
+ scope = node.each_ancestor(:sclass).any? ? "#<Class:#{base}>" : base
292
+ found_method(
293
+ node, "#{humanize_scope(scope)}#{name}", scope_id: anon_block_scope_id(anon_block)
294
+ )
295
+ else
296
+ found_sclass_method(node, name)
297
+ end
298
+ end
278
299
 
279
- # Humanize the scope
300
+ def humanize_scope(scope)
280
301
  scope = scope.sub(
281
302
  /(?:(?<name>.*)::)#<Class:\k<name>>|#<Class:(?<name>.*)>(?:::)?/,
282
303
  '\k<name>.'
283
304
  )
284
- scope << '#' unless scope.end_with?('.')
305
+ scope.end_with?('.') ? scope : "#{scope}#"
306
+ end
307
+
308
+ def anonymous_class_block(node)
309
+ first_block = node.each_ancestor(:block).first
310
+ return unless class_or_module_new_block?(first_block)
311
+ return if first_block.parent&.type?(:lvasgn)
312
+ return if node.each_ancestor(:sclass).any? { |s| !s.children.first.self_type? }
313
+
314
+ first_block
315
+ end
316
+
317
+ def anon_block_scope_id(anon_block)
318
+ parent = anon_block.parent
319
+ return unless parent&.type?(:any_block, :begin, :call, :casgn, :any_def)
320
+
321
+ if (receiver = named_receiver(parent))
322
+ "#{receiver.source}.#{parent.method_name}"
323
+ elsif !parent.begin_type? || parent.parent&.any_block_type?
324
+ source_location(anon_block)
325
+ end
326
+ end
285
327
 
286
- found_method(node, "#{scope}#{name}")
328
+ def named_receiver(node)
329
+ receiver = node.receiver
330
+ receiver unless class_or_module_new_block?(receiver)
287
331
  end
288
332
 
289
333
  def found_sclass_method(node, name)
@@ -296,8 +340,10 @@ module RuboCop
296
340
  found_method(node, "#{singleton_receiver_node.method_name}.#{name}")
297
341
  end
298
342
 
299
- def found_method(node, method_name)
343
+ # rubocop:disable Metrics/AbcSize
344
+ def found_method(node, method_name, scope_id: nil)
300
345
  key = method_key(node, method_name)
346
+ key = "#{key}@#{scope_id}" if scope_id
301
347
  scope = node.each_ancestor(:rescue, :ensure).first&.type
302
348
 
303
349
  if @definitions.key?(key)
@@ -314,6 +360,7 @@ module RuboCop
314
360
  @definitions[key] = node
315
361
  end
316
362
  end
363
+ # rubocop:enable Metrics/AbcSize
317
364
 
318
365
  def method_key(node, method_name)
319
366
  if (ancestor_def = node.each_ancestor(:any_def).first)
@@ -70,7 +70,7 @@ module RuboCop
70
70
 
71
71
  def on_if(node)
72
72
  return if node.body || same_line?(node.loc.begin, node.loc.end)
73
- return if cop_config['AllowComments'] && contains_comments?(node)
73
+ return if allow_comments?(node)
74
74
 
75
75
  range = offense_range(node)
76
76
 
@@ -83,6 +83,11 @@ module RuboCop
83
83
 
84
84
  private
85
85
 
86
+ def allow_comments?(node)
87
+ cop_config['AllowComments'] && contains_comments?(node) &&
88
+ !comments_contain_disables?(node, name)
89
+ end
90
+
86
91
  def offense_range(node)
87
92
  if node.loc.else
88
93
  node.source_range.begin.join(node.loc.else.begin)
@@ -53,11 +53,18 @@ module RuboCop
53
53
  def on_case_match(node)
54
54
  node.in_pattern_branches.each do |branch|
55
55
  next if branch.body
56
- next if cop_config['AllowComments'] && contains_comments?(branch)
56
+ next if allow_comments?(branch)
57
57
 
58
58
  add_offense(branch)
59
59
  end
60
60
  end
61
+
62
+ private
63
+
64
+ def allow_comments?(node)
65
+ cop_config['AllowComments'] && contains_comments?(node) &&
66
+ !comments_contain_disables?(node, name)
67
+ end
61
68
  end
62
69
  end
63
70
  end
@@ -50,11 +50,18 @@ module RuboCop
50
50
  def on_case(node)
51
51
  node.when_branches.each do |when_node|
52
52
  next if when_node.body
53
- next if cop_config['AllowComments'] && contains_comments?(when_node)
53
+ next if allow_comments?(when_node)
54
54
 
55
55
  add_offense(when_node)
56
56
  end
57
57
  end
58
+
59
+ private
60
+
61
+ def allow_comments?(node)
62
+ cop_config['AllowComments'] && contains_comments?(node) &&
63
+ !comments_contain_disables?(node, name)
64
+ end
58
65
  end
59
66
  end
60
67
  end
@@ -162,7 +162,7 @@ module RuboCop
162
162
  end
163
163
 
164
164
  def allow_receiver?(receiver)
165
- if receiver.numeric_type? || (receiver.send_type? &&
165
+ if receiver.numeric_type? || (receiver.call_type? &&
166
166
  (conversion_method?(receiver.method_name) ||
167
167
  allowed_method_name?(receiver.method_name)))
168
168
  true
@@ -62,6 +62,22 @@ module RuboCop
62
62
  # do_something if attrs.respond_to?(:[])
63
63
  #
64
64
  # # bad
65
+ # foo&.bar ? foo&.bar.baz : qux
66
+ #
67
+ # # good
68
+ # foo&.bar ? foo.bar.baz : qux
69
+ #
70
+ # # bad
71
+ # if foo&.bar
72
+ # foo&.bar.baz
73
+ # end
74
+ #
75
+ # # good
76
+ # if foo&.bar
77
+ # foo.bar.baz
78
+ # end
79
+ #
80
+ # # bad
65
81
  # while node&.is_a?(BeginNode)
66
82
  # node = node.parent
67
83
  # end
@@ -37,20 +37,25 @@ module RuboCop
37
37
  }
38
38
  PATTERN
39
39
 
40
+ # rubocop:disable Metrics/AbcSize
40
41
  def on_send(node)
41
42
  return unless require_safe_navigation?(node)
42
43
 
43
44
  bad_method?(node) do |safe_nav, method|
44
45
  return if nil_methods.include?(method) || PLUS_MINUS_METHODS.include?(node.method_name)
46
+ return if ternary_safe_navigation?(node, safe_nav)
45
47
 
46
48
  begin_range = node.loc.dot || safe_nav.source_range.end
47
49
  location = begin_range.join(node.source_range.end)
48
50
 
49
51
  add_offense(location) do |corrector|
52
+ next if ternary_else_branch?(node, safe_nav)
53
+
50
54
  autocorrect(corrector, offense_range: location, send_node: node)
51
55
  end
52
56
  end
53
57
  end
58
+ # rubocop:enable Metrics/AbcSize
54
59
 
55
60
  private
56
61
 
@@ -61,6 +66,18 @@ module RuboCop
61
66
  parent.rhs != node || parent.lhs.receiver != parent.rhs.receiver
62
67
  end
63
68
 
69
+ def ternary_safe_navigation?(node, safe_nav)
70
+ return false unless (parent = node.parent)
71
+
72
+ parent.if_type? && node.equal?(parent.if_branch) && parent.condition == safe_nav
73
+ end
74
+
75
+ def ternary_else_branch?(node, safe_nav)
76
+ return false unless (parent = node.parent)
77
+
78
+ parent.if_type? && node.equal?(parent.else_branch) && parent.condition == safe_nav
79
+ end
80
+
64
81
  # @param [Parser::Source::Range] offense_range
65
82
  # @param [RuboCop::AST::SendNode] send_node
66
83
  # @return [String]
@@ -26,7 +26,31 @@ module RuboCop
26
26
  "#{diagnostic.message}\n(Using Ruby #{ruby_version} parser; " \
27
27
  'configure using `TargetRubyVersion` parameter, under `AllCops`)'
28
28
  end
29
- add_offense(diagnostic.location, message: message, severity: diagnostic.level)
29
+ location = diagnostic_location(diagnostic.location)
30
+ add_offense(location, message: message, severity: diagnostic.level)
31
+ end
32
+
33
+ # Expand zero-length diagnostic ranges so that editors and formatters
34
+ # can display them. This typically occurs when the parser reports
35
+ # `unexpected token $end` at EOF.
36
+ def diagnostic_location(location)
37
+ return location if location.size.positive?
38
+
39
+ source_buffer = location.source_buffer
40
+ if location.end_pos < source_buffer.source.size
41
+ location.resize(1)
42
+ elsif location.begin_pos.positive?
43
+ location.adjust(begin_pos: -1)
44
+ else
45
+ location
46
+ end
47
+ end
48
+
49
+ # Override to skip multiline_ranges check which requires AST.
50
+ # Syntax errors mean the AST is nil, so we go directly to
51
+ # the EOL comment insertion path.
52
+ def disable_offense(offense_range)
53
+ disable_offense_with_eol_or_surround_comment(offense_range)
30
54
  end
31
55
 
32
56
  def add_offense_from_error(error)
@@ -32,6 +32,7 @@ module RuboCop
32
32
  include RangeHelp
33
33
 
34
34
  MSG = 'Avoid leaving a trailing comma in attribute declarations.'
35
+ RESTRICT_ON_SEND = %i[attr_reader attr_writer attr_accessor attr].freeze
35
36
 
36
37
  def on_send(node)
37
38
  return unless node.attribute_accessor? && node.last_argument.def_type?
@@ -95,10 +95,20 @@ module RuboCop
95
95
  return unless variable.method_argument?
96
96
  return if variable.keyword_argument? && cop_config['AllowUnusedKeywordArguments']
97
97
  return if ignored_method?(variable.scope.node.body)
98
+ return if block_argument_with_yield?(variable)
98
99
 
99
100
  super
100
101
  end
101
102
 
103
+ def block_argument_with_yield?(variable)
104
+ return false unless variable.declaration_node.blockarg_type?
105
+
106
+ method_body = variable.scope.node.body
107
+ return false unless method_body
108
+
109
+ method_body.yield_type? || method_body.each_descendant(:yield).any?
110
+ end
111
+
102
112
  def ignored_method?(body)
103
113
  (cop_config['IgnoreEmptyMethods'] && body.nil?) ||
104
114
  (cop_config['IgnoreNotImplementedMethods'] && not_implemented?(body))
@@ -48,12 +48,12 @@ module RuboCop
48
48
 
49
49
  def after_private_modifier?(left_siblings)
50
50
  access_modifier_candidates = left_siblings.compact.select do |left_sibling|
51
- left_sibling.respond_to?(:send_type?) && left_sibling.send_type?
51
+ left_sibling.respond_to?(:bare_access_modifier?) && left_sibling.bare_access_modifier?
52
52
  end
53
53
 
54
- access_modifier_candidates.any? do |candidate|
55
- candidate.command?(:private) && candidate.arguments.none?
56
- end
54
+ return false if access_modifier_candidates.empty?
55
+
56
+ access_modifier_candidates.last.command?(:private)
57
57
  end
58
58
 
59
59
  def private_constantize?(right_siblings, const_value)
@@ -67,6 +67,8 @@ module RuboCop
67
67
  PATTERN
68
68
 
69
69
  def on_send(node)
70
+ return unless node.receiver
71
+
70
72
  unless (prev_arg_node, default_value_node = default_value_argument_and_block(node.parent))
71
73
  return
72
74
  end
@@ -82,28 +82,35 @@ module RuboCop
82
82
  !NIL_METHODS.include?(method_name) && !@additional_nil_methods.include?(method_name)
83
83
  end
84
84
 
85
- # rubocop:disable Metrics/PerceivedComplexity
85
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
86
86
  def sole_condition_of_parent_if?(node)
87
+ child = node
87
88
  parent = node.parent
88
89
 
89
90
  while parent
90
91
  if parent.if_type?
91
- if parent.condition == node
92
- return true
93
- elsif parent.elsif?
94
- parent = find_top_if(parent)
95
- end
92
+ condition = parent.condition
93
+ return true if !child.equal?(condition) && non_nil_condition?(condition, node)
94
+
95
+ parent = find_top_if(parent) if parent.elsif?
96
96
  elsif else_branch?(parent)
97
97
  # Find the top `if` for `else`.
98
98
  parent = parent.parent
99
99
  end
100
100
 
101
+ child = parent
101
102
  parent = parent&.parent
102
103
  end
103
104
 
104
105
  false
105
106
  end
106
- # rubocop:enable Metrics/PerceivedComplexity
107
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
108
+
109
+ def non_nil_condition?(condition, node)
110
+ return true if condition == node
111
+
112
+ condition.csend_type? && csend_root_receiver(condition) == node
113
+ end
107
114
 
108
115
  def else_branch?(node)
109
116
  node.parent&.if_type? && node.parent.else_branch == node
@@ -114,6 +121,14 @@ module RuboCop
114
121
 
115
122
  node
116
123
  end
124
+
125
+ def csend_root_receiver(node)
126
+ return unless (receiver = node.receiver)
127
+
128
+ receiver = receiver.receiver while receiver.call_type? && receiver.receiver
129
+
130
+ receiver
131
+ end
117
132
  end
118
133
  end
119
134
  end
@@ -38,7 +38,7 @@ module RuboCop
38
38
  end
39
39
 
40
40
  def safe_to_split?(node)
41
- node.each_descendant(:if, :case, :kwbegin, :any_def).none? &&
41
+ node.each_descendant(:if, :case, :kwbegin, :any_def, :rescue, :ensure).none? &&
42
42
  node.each_descendant(:dstr, :str).none? { |n| n.heredoc? || n.value.include?("\n") } &&
43
43
  node.each_descendant(:begin, :sym).none?(&:multiline?)
44
44
  end
@@ -16,7 +16,7 @@ module RuboCop
16
16
  end
17
17
 
18
18
  # Registry that tracks all cops by their badge and department.
19
- class Registry
19
+ class Registry # rubocop:disable Metrics/ClassLength
20
20
  include Enumerable
21
21
 
22
22
  def self.all
@@ -46,7 +46,7 @@ module RuboCop
46
46
  global.qualify_badge(badge).first == badge
47
47
  end
48
48
 
49
- attr_reader :options
49
+ attr_reader :options, :warnings
50
50
 
51
51
  def initialize(cops = [], options = {})
52
52
  @registry = {}
@@ -58,6 +58,7 @@ module RuboCop
58
58
 
59
59
  @enabled_cache = {}.compare_by_identity
60
60
  @disabled_cache = {}.compare_by_identity
61
+ @warnings = {}
61
62
  end
62
63
 
63
64
  def enlist(cop)
@@ -132,7 +133,7 @@ module RuboCop
132
133
  # @return [String] Qualified cop name
133
134
  def qualified_cop_name(name, path, warn: true)
134
135
  badge = Badge.parse(name)
135
- print_warning(name, path) if warn && department_missing?(badge, name)
136
+ print_department_missing_warning(name, path) if warn && department_missing?(badge, name)
136
137
  return name if registered?(badge)
137
138
 
138
139
  potential_badges = qualify_badge(badge)
@@ -148,12 +149,12 @@ module RuboCop
148
149
  !badge.qualified? && unqualified_cop_names.include?(name)
149
150
  end
150
151
 
151
- def print_warning(name, path)
152
- message = "#{path}: Warning: no department given for #{name}."
152
+ def print_department_missing_warning(name, path)
153
+ message = "no department given for #{name}."
153
154
  if path.end_with?('.rb')
154
155
  message += ' Run `rubocop -a --only Migration/DepartmentName` to fix.'
155
156
  end
156
- warn message
157
+ emit_warning(path, message)
157
158
  end
158
159
 
159
160
  def unqualified_cop_names
@@ -274,6 +275,10 @@ module RuboCop
274
275
  attr_reader :global
275
276
  end
276
277
 
278
+ def warnings?(path)
279
+ @warnings[path]
280
+ end
281
+
277
282
  private
278
283
 
279
284
  def initialize_copy(reg)
@@ -297,18 +302,20 @@ module RuboCop
297
302
  end
298
303
 
299
304
  def resolve_badge(given_badge, real_badge, source_path, warn: true)
300
- unless given_badge.match?(real_badge)
301
- path = PathUtil.smart_path(source_path)
302
-
303
- if warn
304
- warn("#{path}: #{given_badge} has the wrong namespace - " \
305
- "replace it with #{given_badge.with_department(real_badge.department)}")
306
- end
305
+ if warn && !given_badge.match?(real_badge)
306
+ emit_warning(source_path,
307
+ "#{given_badge} has the wrong namespace - " \
308
+ "replace it with #{given_badge.with_department(real_badge.department)}")
307
309
  end
308
310
 
309
311
  real_badge.to_s
310
312
  end
311
313
 
314
+ def emit_warning(path, message)
315
+ Registry.global.warnings[path] = true
316
+ warn "#{PathUtil.smart_path(path)}: Warning: #{message}"
317
+ end
318
+
312
319
  def registered?(badge)
313
320
  clear_enrollment_queue
314
321
  @registry.key?(badge)
@@ -348,8 +348,20 @@ module RuboCop
348
348
 
349
349
  def remove_modifier_node_within_begin(corrector, modifier_node, begin_node)
350
350
  def_node = begin_node.children[begin_node.children.index(modifier_node) + 1]
351
- range = modifier_node.source_range.begin.join(def_node.source_range.begin)
352
- corrector.remove(range)
351
+ # Stop the removal range at the first comment that precedes the def, if
352
+ # any exist. Without this, comments between the modifier and the def are
353
+ # dropped because they fall inside the removed range.
354
+ end_pos = first_comment_or_node_start(def_node)
355
+ corrector.remove(modifier_node.source_range.begin.join(end_pos))
356
+ end
357
+
358
+ def first_comment_or_node_start(node)
359
+ preceding = processed_source.ast_with_comments[node].select do |comment|
360
+ comment.loc.line < node.loc.line
361
+ end
362
+ return node.source_range.begin if preceding.empty?
363
+
364
+ preceding.first.source_range.begin
353
365
  end
354
366
 
355
367
  def def_source(node, def_nodes)
@@ -128,6 +128,7 @@ module RuboCop
128
128
 
129
129
  def correct_other(node, corrector)
130
130
  return if node.parenthesized_call?
131
+ return if node.children.empty? && node.equal?(node.parent.children.last)
131
132
 
132
133
  corrector.wrap(node, '(', ')')
133
134
  end
@@ -424,21 +424,39 @@ module RuboCop
424
424
  end
425
425
 
426
426
  def forwarded_rest_arg
427
- return nil if referenced_rest_arg?
427
+ return @forwarded_rest_arg if defined?(@forwarded_rest_arg)
428
428
 
429
- arguments.find { |arg| forwarded_rest_arg?(arg, @rest_arg_name) }
429
+ @forwarded_rest_arg = if referenced_rest_arg?
430
+ nil
431
+ else
432
+ arguments.find do |arg|
433
+ forwarded_rest_arg?(arg, @rest_arg_name)
434
+ end
435
+ end
430
436
  end
431
437
 
432
438
  def forwarded_kwrest_arg
433
- return nil if referenced_kwrest_arg?
439
+ return @forwarded_kwrest_arg if defined?(@forwarded_kwrest_arg)
434
440
 
435
- arguments.filter_map { |arg| extract_forwarded_kwrest_arg(arg, @kwrest_arg_name) }.first
441
+ @forwarded_kwrest_arg = if referenced_kwrest_arg?
442
+ nil
443
+ else
444
+ arguments.filter_map do |arg|
445
+ extract_forwarded_kwrest_arg(arg, @kwrest_arg_name)
446
+ end.first
447
+ end
436
448
  end
437
449
 
438
450
  def forwarded_block_arg
439
- return nil if referenced_block_arg?
440
-
441
- arguments.find { |arg| forwarded_block_arg?(arg, @block_arg_name) }
451
+ return @forwarded_block_arg if defined?(@forwarded_block_arg)
452
+
453
+ @forwarded_block_arg = if referenced_block_arg?
454
+ nil
455
+ else
456
+ arguments.find do |arg|
457
+ forwarded_block_arg?(arg, @block_arg_name)
458
+ end
459
+ end
442
460
  end
443
461
 
444
462
  def classification
@@ -52,7 +52,7 @@ module RuboCop
52
52
  end
53
53
 
54
54
  def allowed_non_ascii_chars
55
- cop_config['AllowedChars'] || []
55
+ @allowed_non_ascii_chars ||= (cop_config['AllowedChars'] || []).freeze
56
56
  end
57
57
  end
58
58
  end