rubocop 1.20.0 → 1.21.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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +19 -6
  4. data/lib/rubocop/cop/base.rb +2 -2
  5. data/lib/rubocop/cop/bundler/insecure_protocol_source.rb +12 -11
  6. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +2 -2
  7. data/lib/rubocop/cop/correctors/line_break_corrector.rb +1 -1
  8. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
  9. data/lib/rubocop/cop/layout/class_structure.rb +2 -1
  10. data/lib/rubocop/cop/layout/end_alignment.rb +1 -1
  11. data/lib/rubocop/cop/layout/leading_comment_space.rb +1 -1
  12. data/lib/rubocop/cop/layout/line_length.rb +1 -1
  13. data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -1
  14. data/lib/rubocop/cop/layout/single_line_block_chain.rb +15 -4
  15. data/lib/rubocop/cop/layout/space_after_not.rb +1 -0
  16. data/lib/rubocop/cop/layout/space_around_equals_in_parameter_default.rb +2 -1
  17. data/lib/rubocop/cop/layout/space_around_keyword.rb +2 -2
  18. data/lib/rubocop/cop/layout/space_before_brackets.rb +1 -0
  19. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +1 -1
  20. data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +107 -0
  21. data/lib/rubocop/cop/lint/ambiguous_range.rb +1 -1
  22. data/lib/rubocop/cop/lint/debugger.rb +0 -2
  23. data/lib/rubocop/cop/lint/empty_in_pattern.rb +1 -1
  24. data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
  25. data/lib/rubocop/cop/lint/float_out_of_range.rb +1 -1
  26. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +67 -0
  27. data/lib/rubocop/cop/lint/number_conversion.rb +7 -1
  28. data/lib/rubocop/cop/lint/unused_method_argument.rb +2 -3
  29. data/lib/rubocop/cop/lint/useless_times.rb +1 -1
  30. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
  31. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +1 -1
  32. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  33. data/lib/rubocop/cop/mixin/code_length.rb +1 -1
  34. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +11 -6
  35. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +2 -2
  36. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +9 -1
  37. data/lib/rubocop/cop/naming/ascii_identifiers.rb +0 -3
  38. data/lib/rubocop/cop/naming/constant_name.rb +1 -1
  39. data/lib/rubocop/cop/naming/inclusive_language.rb +9 -9
  40. data/lib/rubocop/cop/style/accessor_grouping.rb +2 -2
  41. data/lib/rubocop/cop/style/and_or.rb +4 -0
  42. data/lib/rubocop/cop/style/ascii_comments.rb +0 -3
  43. data/lib/rubocop/cop/style/case_equality.rb +6 -9
  44. data/lib/rubocop/cop/style/collection_methods.rb +2 -1
  45. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +1 -1
  46. data/lib/rubocop/cop/style/documentation.rb +23 -8
  47. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  48. data/lib/rubocop/cop/style/explicit_block_argument.rb +21 -11
  49. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +3 -2
  50. data/lib/rubocop/cop/style/lambda_call.rb +1 -1
  51. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +6 -6
  52. data/lib/rubocop/cop/style/mutable_constant.rb +1 -1
  53. data/lib/rubocop/cop/style/negated_if.rb +1 -1
  54. data/lib/rubocop/cop/style/negated_unless.rb +1 -1
  55. data/lib/rubocop/cop/style/non_nil_check.rb +2 -2
  56. data/lib/rubocop/cop/style/not.rb +2 -2
  57. data/lib/rubocop/cop/style/parallel_assignment.rb +1 -1
  58. data/lib/rubocop/cop/style/percent_q_literals.rb +2 -2
  59. data/lib/rubocop/cop/style/raise_args.rb +1 -1
  60. data/lib/rubocop/cop/style/redundant_condition.rb +2 -3
  61. data/lib/rubocop/cop/style/redundant_interpolation.rb +1 -1
  62. data/lib/rubocop/cop/style/redundant_percent_q.rb +2 -3
  63. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +1 -1
  64. data/lib/rubocop/cop/style/redundant_sort.rb +17 -2
  65. data/lib/rubocop/cop/style/regexp_literal.rb +3 -3
  66. data/lib/rubocop/cop/style/return_nil.rb +2 -1
  67. data/lib/rubocop/cop/style/static_class.rb +1 -2
  68. data/lib/rubocop/cop/style/string_concatenation.rb +1 -1
  69. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  70. data/lib/rubocop/cop/style/yoda_condition.rb +4 -7
  71. data/lib/rubocop/result_cache.rb +1 -1
  72. data/lib/rubocop/runner.rb +1 -2
  73. data/lib/rubocop/version.rb +1 -1
  74. data/lib/rubocop.rb +3 -1
  75. metadata +5 -3
@@ -51,7 +51,7 @@ module RuboCop
51
51
 
52
52
  def on_case_match(node)
53
53
  node.in_pattern_branches.each do |branch|
54
- next if branch.body || cop_config['AllowComments'] && comment_lines?(node)
54
+ next if branch.body || (cop_config['AllowComments'] && comment_lines?(node))
55
55
 
56
56
  add_offense(branch)
57
57
  end
@@ -118,7 +118,7 @@ module RuboCop
118
118
  end
119
119
 
120
120
  def correct_arguments?(arguments)
121
- arguments.size == 1 || arguments.size == 2 && arguments[1].hash_type?
121
+ arguments.size == 1 || (arguments.size == 2 && arguments[1].hash_type?)
122
122
  end
123
123
 
124
124
  def build_kwargs(node)
@@ -24,7 +24,7 @@ module RuboCop
24
24
  def on_float(node)
25
25
  value, = *node
26
26
 
27
- return unless value.infinite? || value.zero? && /[1-9]/.match?(node.source)
27
+ return unless value.infinite? || (value.zero? && /[1-9]/.match?(node.source))
28
28
 
29
29
  add_offense(node)
30
30
  end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ #
7
+ # This cop checks for `IO.select` that is incompatible with Fiber Scheduler since Ruby 3.0.
8
+ #
9
+ # @example
10
+ #
11
+ # # bad
12
+ # IO.select([io], [], [], timeout)
13
+ #
14
+ # # good
15
+ # io.wait_readable(timeout)
16
+ #
17
+ # # bad
18
+ # IO.select([], [io], [], timeout)
19
+ #
20
+ # # good
21
+ # io.wait_writable(timeout)
22
+ #
23
+ class IncompatibleIoSelectWithFiberScheduler < Base
24
+ extend AutoCorrector
25
+
26
+ MSG = 'Use `%<preferred>s` instead of `%<current>s`.'
27
+ RESTRICT_ON_SEND = %i[select].freeze
28
+
29
+ # @!method io_select(node)
30
+ def_node_matcher :io_select, <<~PATTERN
31
+ (send
32
+ (const {nil? cbase} :IO) :select $_ $_ {(array) nil} $...)
33
+ PATTERN
34
+
35
+ def on_send(node)
36
+ return unless (read, write, timeout = io_select(node))
37
+ return unless scheduler_compatible?(read, write) || scheduler_compatible?(write, read)
38
+
39
+ preferred = preferred_method(read, write, timeout)
40
+ message = format(MSG, preferred: preferred, current: node.source)
41
+
42
+ add_offense(node, message: message) do |corrector|
43
+ corrector.replace(node, preferred)
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def scheduler_compatible?(io1, io2)
50
+ return false unless io1.array_type? && io1.values.size == 1
51
+
52
+ io2.array_type? ? io2.values.empty? : io2.nil_type?
53
+ end
54
+
55
+ def preferred_method(read, write, timeout)
56
+ timeout_argument = timeout.empty? ? '' : "(#{timeout[0].source})"
57
+
58
+ if read.array_type? && read.values[0]
59
+ "#{read.values[0].source}.wait_readable#{timeout_argument}"
60
+ else
61
+ "#{write.values[0].source}.wait_writable#{timeout_argument}"
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -60,6 +60,7 @@ module RuboCop
60
60
  'class parsing, instead of using '\
61
61
  '`%<current>s`, use stricter '\
62
62
  '`%<corrected_method>s`.'
63
+ CONVERSION_METHODS = %i[Integer Float Complex to_i to_f to_c].freeze
63
64
  METHODS = CONVERSION_METHOD_CLASS_MAPPING.keys.map(&:inspect).join(' ')
64
65
 
65
66
  # @!method to_method(node)
@@ -127,7 +128,8 @@ module RuboCop
127
128
  end
128
129
 
129
130
  def ignore_receiver?(receiver)
130
- if receiver.send_type? && ignored_method?(receiver.method_name)
131
+ if receiver.numeric_type? || (receiver.send_type? &&
132
+ (conversion_method?(receiver.method_name) || ignored_method?(receiver.method_name)))
131
133
  true
132
134
  elsif (receiver = top_receiver(receiver))
133
135
  receiver.const_type? && ignored_class?(receiver.const_name)
@@ -142,6 +144,10 @@ module RuboCop
142
144
  receiver
143
145
  end
144
146
 
147
+ def conversion_method?(method_name)
148
+ CONVERSION_METHODS.include?(method_name)
149
+ end
150
+
145
151
  def ignored_classes
146
152
  cop_config.fetch('IgnoredClasses', [])
147
153
  end
@@ -87,9 +87,8 @@ module RuboCop
87
87
  end
88
88
 
89
89
  def ignored_method?(body)
90
- cop_config['IgnoreEmptyMethods'] && body.nil? ||
91
- cop_config['IgnoreNotImplementedMethods'] &&
92
- not_implemented?(body)
90
+ (cop_config['IgnoreEmptyMethods'] && body.nil?) ||
91
+ (cop_config['IgnoreNotImplementedMethods'] && not_implemented?(body))
93
92
  end
94
93
 
95
94
  def message(variable)
@@ -65,7 +65,7 @@ module RuboCop
65
65
  private
66
66
 
67
67
  def never_process?(count, node)
68
- count < 1 || node.block_type? && node.body.nil?
68
+ count < 1 || (node.block_type? && node.body.nil?)
69
69
  end
70
70
 
71
71
  def remove_node(corrector, node)
@@ -45,7 +45,7 @@ module RuboCop
45
45
  else
46
46
  # Otherwise, the case node gets 0.8 complexity points and each
47
47
  # when gets 0.2.
48
- (0.8 + 0.2 * nb_branches).round
48
+ (0.8 + (0.2 * nb_branches)).round
49
49
  end
50
50
  when :if
51
51
  node.else? && !node.elsif? ? 2 : 1
@@ -46,7 +46,7 @@ module RuboCop
46
46
  visit_depth_last(@node) { |child| calculate_node(child) }
47
47
 
48
48
  [
49
- Math.sqrt(@assignment**2 + @branch**2 + @condition**2).round(2),
49
+ Math.sqrt((@assignment**2) + (@branch**2) + (@condition**2)).round(2),
50
50
  "<#{@assignment}, #{@branch}, #{@condition}>"
51
51
  ]
52
52
  end
@@ -147,7 +147,7 @@ module RuboCop
147
147
 
148
148
  # Returns true for lines that shall not be included in the count.
149
149
  def irrelevant_line?(source_line)
150
- source_line.blank? || !count_comments? && comment_line?(source_line)
150
+ source_line.blank? || (!count_comments? && comment_line?(source_line))
151
151
  end
152
152
 
153
153
  def count_comments?
@@ -43,7 +43,7 @@ module RuboCop
43
43
 
44
44
  # Returns true for lines that shall not be included in the count.
45
45
  def irrelevant_line(source_line)
46
- source_line.blank? || !count_comments? && comment_line?(source_line)
46
+ source_line.blank? || (!count_comments? && comment_line?(source_line))
47
47
  end
48
48
 
49
49
  def build_code_length_calculator(node)
@@ -9,9 +9,8 @@ module RuboCop
9
9
  FROZEN_STRING_LITERAL = '# frozen_string_literal:'
10
10
  FROZEN_STRING_LITERAL_ENABLED = '# frozen_string_literal: true'
11
11
  FROZEN_STRING_LITERAL_TYPES_RUBY27 = %i[str dstr].freeze
12
- FROZEN_STRING_LITERAL_TYPES_RUBY30 = %i[str].freeze
13
12
 
14
- private_constant :FROZEN_STRING_LITERAL_TYPES_RUBY27, :FROZEN_STRING_LITERAL_TYPES_RUBY30
13
+ private_constant :FROZEN_STRING_LITERAL_TYPES_RUBY27
15
14
 
16
15
  def frozen_string_literal_comment_exists?
17
16
  leading_comment_lines.any? { |line| MagicComment.parse(line).valid_literal_value? }
@@ -20,13 +19,19 @@ module RuboCop
20
19
  private
21
20
 
22
21
  def frozen_string_literal?(node)
23
- literal_types = if target_ruby_version >= 3.0
24
- FROZEN_STRING_LITERAL_TYPES_RUBY30
22
+ frozen_string = if target_ruby_version >= 3.0
23
+ node.str_type? || frozen_heredoc?(node)
25
24
  else
26
- FROZEN_STRING_LITERAL_TYPES_RUBY27
25
+ FROZEN_STRING_LITERAL_TYPES_RUBY27.include?(node.type)
27
26
  end
28
27
 
29
- literal_types.include?(node.type) && frozen_string_literals_enabled?
28
+ frozen_string && frozen_string_literals_enabled?
29
+ end
30
+
31
+ def frozen_heredoc?(node)
32
+ return false unless node.dstr_type? && node.heredoc?
33
+
34
+ node.children.all?(&:str_type?)
30
35
  end
31
36
 
32
37
  def frozen_string_literals_enabled?
@@ -134,7 +134,7 @@ module RuboCop
134
134
 
135
135
  next if a.setter_method?
136
136
  next unless kind == :with_or_without_parentheses ||
137
- kind == :with_parentheses && parentheses?(a)
137
+ (kind == :with_parentheses && parentheses?(a))
138
138
 
139
139
  a.arguments.any? { |arg| within_node?(node, arg) }
140
140
  end
@@ -156,7 +156,7 @@ module RuboCop
156
156
 
157
157
  def disqualified_rhs?(candidate, ancestor)
158
158
  UNALIGNED_RHS_TYPES.include?(ancestor.type) ||
159
- ancestor.block_type? && part_of_block_body?(candidate, ancestor)
159
+ (ancestor.block_type? && part_of_block_body?(candidate, ancestor))
160
160
  end
161
161
 
162
162
  def valid_rhs?(candidate, ancestor)
@@ -93,7 +93,15 @@ module RuboCop
93
93
  end
94
94
 
95
95
  def aligned_assignment?(range, line)
96
- range.source[-1] == '=' && line[range.last_column - 1] == '='
96
+ (range.source[-1] == '=' && line[range.last_column - 1] == '=') ||
97
+ aligned_with_append_operator?(range, line)
98
+ end
99
+
100
+ def aligned_with_append_operator?(range, line)
101
+ last_column = range.last_column
102
+
103
+ (range.source == '<<' && line[last_column - 1] == '=') ||
104
+ (range.source[-1] == '=' && line[(last_column - 2)..(last_column - 1)] == '<<')
97
105
  end
98
106
 
99
107
  def aligned_identical?(range, line)
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # rubocop:disable Style/AsciiComments
4
-
5
3
  module RuboCop
6
4
  module Cop
7
5
  module Naming
@@ -90,4 +88,3 @@ module RuboCop
90
88
  end
91
89
  end
92
90
  end
93
- # rubocop:enable Style/AsciiComments
@@ -54,7 +54,7 @@ module RuboCop
54
54
  private
55
55
 
56
56
  def allowed_assignment?(value)
57
- value && %i[block const casgn].include?(value.type) ||
57
+ (value && %i[block const casgn].include?(value.type)) ||
58
58
  allowed_method_call_on_rhs?(value) ||
59
59
  class_or_struct_return_method?(value) ||
60
60
  allowed_conditional_expression_on_rhs?(value)
@@ -70,6 +70,8 @@ module RuboCop
70
70
  include RangeHelp
71
71
 
72
72
  EMPTY_ARRAY = [].freeze
73
+ MSG = "Consider replacing '%<term>s'%<suffix>s."
74
+ MSG_FOR_FILE_PATH = "Consider replacing '%<term>s' in file path%<suffix>s."
73
75
 
74
76
  WordLocation = Struct.new(:word, :position)
75
77
 
@@ -197,12 +199,11 @@ module RuboCop
197
199
  end
198
200
 
199
201
  def create_single_word_message_for_file(word)
200
- create_message(word).sub(/\.$/, ' in file path.')
202
+ create_message(word, MSG_FOR_FILE_PATH)
201
203
  end
202
204
 
203
205
  def create_multiple_word_message_for_file(words)
204
- quoted_words = words.map { |word| "'#{word}'" }
205
- "Consider replacing problematic terms #{quoted_words.join(', ')} in file path."
206
+ format(MSG_FOR_FILE_PATH, term: words.join("', '"), suffix: ' with other terms')
206
207
  end
207
208
 
208
209
  def scan_for_words(input)
@@ -223,9 +224,12 @@ module RuboCop
223
224
  safe_str.gsub(@allowed_regex) { |match| '*' * match.size }
224
225
  end
225
226
 
226
- def create_message(word)
227
+ def create_message(word, message = MSG)
227
228
  flagged_term = find_flagged_term(word)
228
- "Consider replacing problematic term '#{word}'#{flagged_term['SuggestionString']}."
229
+ suggestions = flagged_term['SuggestionString']
230
+ suggestions = ' with another term' if suggestions.blank?
231
+
232
+ format(message, term: word, suffix: suggestions)
229
233
  end
230
234
 
231
235
  def find_flagged_term(word)
@@ -235,10 +239,6 @@ module RuboCop
235
239
  flagged_term
236
240
  end
237
241
 
238
- def create_message_for_file(word)
239
- create_message(word).sub(/\.$/, ' in file path.')
240
- end
241
-
242
242
  def preprocess_suggestions(suggestions)
243
243
  return '' if suggestions.nil? ||
244
244
  (suggestions.is_a?(String) && suggestions.strip.empty?) || suggestions.empty?
@@ -59,8 +59,8 @@ module RuboCop
59
59
 
60
60
  def check(send_node)
61
61
  return if previous_line_comment?(send_node)
62
- return unless grouped_style? && sibling_accessors(send_node).size > 1 ||
63
- separated_style? && send_node.arguments.size > 1
62
+ return unless (grouped_style? && sibling_accessors(send_node).size > 1) ||
63
+ (separated_style? && send_node.arguments.size > 1)
64
64
 
65
65
  message = message(send_node)
66
66
  add_offense(send_node, message: message) do |corrector|
@@ -7,6 +7,10 @@ module RuboCop
7
7
  # `||` instead. It can be configured to check only in conditions or in
8
8
  # all contexts.
9
9
  #
10
+ # It is marked as unsafe auto-correction because it may change the
11
+ # operator precedence between logical operators (`&&` and `||`) and
12
+ # semantic operators (`and` and `or`).
13
+ #
10
14
  # @example EnforcedStyle: always
11
15
  # # bad
12
16
  # foo.save and return
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # rubocop:disable Style/AsciiComments
4
-
5
3
  module RuboCop
6
4
  module Cop
7
5
  module Style
@@ -57,4 +55,3 @@ module RuboCop
57
55
  end
58
56
  end
59
57
  end
60
- # rubocop:enable Style/AsciiComments
@@ -5,9 +5,11 @@ module RuboCop
5
5
  module Style
6
6
  # This cop checks for uses of the case equality operator(===).
7
7
  #
8
+ # If `AllowOnConstant` option is enabled, the cop will ignore violations when the receiver of
9
+ # the case equality operator is a constant.
10
+ #
8
11
  # @example
9
12
  # # bad
10
- # Array === something
11
13
  # (1..100) === 7
12
14
  # /something/ === some_string
13
15
  #
@@ -16,18 +18,13 @@ module RuboCop
16
18
  # (1..100).include?(7)
17
19
  # /something/.match?(some_string)
18
20
  #
19
- # @example AllowOnConstant
20
- # # Style/CaseEquality:
21
- # # AllowOnConstant: true
22
- #
21
+ # @example AllowOnConstant: false (default)
23
22
  # # bad
24
- # (1..100) === 7
25
- # /something/ === some_string
23
+ # Array === something
26
24
  #
25
+ # @example AllowOnConstant: true
27
26
  # # good
28
27
  # Array === something
29
- # (1..100).include?(7)
30
- # /something/.match?(some_string)
31
28
  #
32
29
  class CaseEquality < Base
33
30
  extend AutoCorrector
@@ -68,7 +68,8 @@ module RuboCop
68
68
  return false unless node.arguments.any?
69
69
 
70
70
  node.last_argument.block_pass_type? ||
71
- node.last_argument.sym_type? && methods_accepting_symbol.include?(node.method_name.to_s)
71
+ (node.last_argument.sym_type? &&
72
+ methods_accepting_symbol.include?(node.method_name.to_s))
72
73
  end
73
74
 
74
75
  def message(node)
@@ -86,7 +86,7 @@ module RuboCop
86
86
 
87
87
  return unless arg_node&.dstr_type? && interpolated?(arg_node)
88
88
  return if inline_comment_docs?(arg_node) ||
89
- arg_node.heredoc? && comment_block_docs?(arg_node)
89
+ (arg_node.heredoc? && comment_block_docs?(arg_node))
90
90
 
91
91
  add_offense(node.loc.selector)
92
92
  end
@@ -71,8 +71,9 @@ module RuboCop
71
71
  #
72
72
  class Documentation < Base
73
73
  include DocumentationComment
74
+ include RangeHelp
74
75
 
75
- MSG = 'Missing top-level %<type>s documentation comment.'
76
+ MSG = 'Missing top-level documentation comment for `%<type>s %<identifier>s`.'
76
77
 
77
78
  # @!method constant_definition?(node)
78
79
  def_node_matcher :constant_definition?, '{class module casgn}'
@@ -88,33 +89,35 @@ module RuboCop
88
89
  def on_class(node)
89
90
  return unless node.body
90
91
 
91
- check(node, node.body, :class)
92
+ check(node, node.body)
92
93
  end
93
94
 
94
95
  def on_module(node)
95
- check(node, node.body, :module)
96
+ check(node, node.body)
96
97
  end
97
98
 
98
99
  private
99
100
 
100
- def check(node, body, type)
101
+ def check(node, body)
101
102
  return if namespace?(body)
102
103
  return if documentation_comment?(node)
103
104
  return if constant_allowed?(node)
104
105
  return if nodoc_self_or_outer_module?(node)
105
106
  return if macro_only?(body)
106
107
 
107
- add_offense(node.loc.keyword, message: format(MSG, type: type))
108
+ range = range_between(node.loc.expression.begin_pos, node.loc.name.end_pos)
109
+ message = format(MSG, type: node.type, identifier: identifier(node))
110
+ add_offense(range, message: message)
108
111
  end
109
112
 
110
113
  def nodoc_self_or_outer_module?(node)
111
114
  nodoc_comment?(node) ||
112
- compact_namespace?(node) && nodoc_comment?(outer_module(node).first)
115
+ (compact_namespace?(node) && nodoc_comment?(outer_module(node).first))
113
116
  end
114
117
 
115
118
  def macro_only?(body)
116
- body.respond_to?(:macro?) && body.macro? ||
117
- body.respond_to?(:children) && body.children&.all? { |child| macro_only?(child) }
119
+ (body.respond_to?(:macro?) && body.macro?) ||
120
+ (body.respond_to?(:children) && body.children&.all? { |child| macro_only?(child) })
118
121
  end
119
122
 
120
123
  def namespace?(node)
@@ -165,6 +168,18 @@ module RuboCop
165
168
  def allowed_constants
166
169
  @allowed_constants ||= cop_config.fetch('AllowedConstants', []).map(&:intern)
167
170
  end
171
+
172
+ def identifier(node)
173
+ # Get the fully qualified identifier for a class/module
174
+ nodes = [node, *node.each_ancestor(:class, :module)]
175
+ nodes.reverse_each.flat_map { |n| qualify_const(n.identifier) }.join('::')
176
+ end
177
+
178
+ def qualify_const(node)
179
+ return if node.nil?
180
+
181
+ [qualify_const(node.namespace), node.short_name].compact
182
+ end
168
183
  end
169
184
  end
170
185
  end