rubocop 1.41.0 → 1.42.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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +2 -2
  4. data/config/default.yml +26 -1
  5. data/lib/rubocop/cli.rb +1 -1
  6. data/lib/rubocop/config.rb +7 -7
  7. data/lib/rubocop/config_loader_resolver.rb +5 -1
  8. data/lib/rubocop/cop/base.rb +62 -61
  9. data/lib/rubocop/cop/cop.rb +28 -28
  10. data/lib/rubocop/cop/corrector.rb +23 -11
  11. data/lib/rubocop/cop/gemspec/dependency_version.rb +16 -18
  12. data/lib/rubocop/cop/layout/class_structure.rb +32 -11
  13. data/lib/rubocop/cop/layout/comment_indentation.rb +3 -1
  14. data/lib/rubocop/cop/layout/indentation_style.rb +4 -1
  15. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +6 -6
  16. data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -1
  17. data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
  18. data/lib/rubocop/cop/layout/trailing_whitespace.rb +5 -2
  19. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +19 -0
  20. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +3 -1
  21. data/lib/rubocop/cop/lint/regexp_as_condition.rb +6 -0
  22. data/lib/rubocop/cop/lint/require_parentheses.rb +3 -1
  23. data/lib/rubocop/cop/lint/unused_method_argument.rb +2 -1
  24. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +5 -3
  25. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
  26. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +4 -4
  27. data/lib/rubocop/cop/mixin/annotation_comment.rb +1 -1
  28. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +1 -1
  29. data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
  30. data/lib/rubocop/cop/registry.rb +22 -22
  31. data/lib/rubocop/cop/security/compound_hash.rb +2 -1
  32. data/lib/rubocop/cop/style/alias.rb +9 -1
  33. data/lib/rubocop/cop/style/block_comments.rb +1 -1
  34. data/lib/rubocop/cop/style/concat_array_literals.rb +22 -2
  35. data/lib/rubocop/cop/style/documentation.rb +10 -4
  36. data/lib/rubocop/cop/style/guard_clause.rb +12 -8
  37. data/lib/rubocop/cop/style/hash_syntax.rb +10 -7
  38. data/lib/rubocop/cop/style/identical_conditional_branches.rb +15 -0
  39. data/lib/rubocop/cop/style/map_to_set.rb +61 -0
  40. data/lib/rubocop/cop/style/method_def_parentheses.rb +11 -4
  41. data/lib/rubocop/cop/style/min_max_comparison.rb +73 -0
  42. data/lib/rubocop/cop/style/redundant_string_escape.rb +6 -3
  43. data/lib/rubocop/cop/style/require_order.rb +4 -2
  44. data/lib/rubocop/cop/style/select_by_regexp.rb +6 -2
  45. data/lib/rubocop/cop/style/signal_exception.rb +8 -6
  46. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +4 -4
  47. data/lib/rubocop/cop/style/word_array.rb +41 -0
  48. data/lib/rubocop/cop/style/yoda_expression.rb +74 -0
  49. data/lib/rubocop/cop/style/zero_length_predicate.rb +31 -14
  50. data/lib/rubocop/cop/team.rb +29 -29
  51. data/lib/rubocop/cop/variable_force.rb +0 -3
  52. data/lib/rubocop/cops_documentation_generator.rb +11 -8
  53. data/lib/rubocop/formatter.rb +2 -0
  54. data/lib/rubocop/path_util.rb +6 -1
  55. data/lib/rubocop/result_cache.rb +1 -1
  56. data/lib/rubocop/runner.rb +10 -3
  57. data/lib/rubocop/target_ruby.rb +0 -1
  58. data/lib/rubocop/version.rb +1 -1
  59. data/lib/rubocop.rb +3 -0
  60. metadata +12 -9
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Enforces the use of `max` or `min` instead of comparison for greater or less.
7
+ #
8
+ # NOTE: It can be used if you want to present limit or threshold in Ruby 2.7+.
9
+ # That it is slow though. So autocorrection will apply generic `max` or `min`:
10
+ #
11
+ # [source,ruby]
12
+ # ----
13
+ # a.clamp(b..) # Same as `[a, b].max`
14
+ # a.clamp(..b) # Same as `[a, b].min`
15
+ # ----
16
+ #
17
+ # @safety
18
+ # This cop is unsafe because even if a value has `<` or `>` method,
19
+ # it is not necessarily `Comparable`.
20
+ #
21
+ # @example
22
+ #
23
+ # # bad
24
+ # a > b ? a : b
25
+ # a >= b ? a : b
26
+ #
27
+ # # good
28
+ # [a, b].max
29
+ #
30
+ # # bad
31
+ # a < b ? a : b
32
+ # a <= b ? a : b
33
+ #
34
+ # # good
35
+ # [a, b].min
36
+ #
37
+ class MinMaxComparison < Base
38
+ extend AutoCorrector
39
+
40
+ MSG = 'Use `%<prefer>s` instead.'
41
+ GRATER_OPERATORS = %i[> >=].freeze
42
+ LESS_OPERATORS = %i[< <=].freeze
43
+ COMPARISON_OPERATORS = GRATER_OPERATORS + LESS_OPERATORS
44
+
45
+ def on_if(node)
46
+ lhs, operator, rhs = *node.condition
47
+ return unless COMPARISON_OPERATORS.include?(operator)
48
+
49
+ if_branch = node.if_branch
50
+ else_branch = node.else_branch
51
+ preferred_method = preferred_method(operator, lhs, rhs, if_branch, else_branch)
52
+ return unless preferred_method
53
+
54
+ replacement = "[#{lhs.source}, #{rhs.source}].#{preferred_method}"
55
+
56
+ add_offense(node, message: format(MSG, prefer: replacement)) do |corrector|
57
+ corrector.replace(node, replacement)
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def preferred_method(operator, lhs, rhs, if_branch, else_branch)
64
+ if lhs == if_branch && rhs == else_branch
65
+ GRATER_OPERATORS.include?(operator) ? 'max' : 'min'
66
+ elsif lhs == else_branch && rhs == if_branch
67
+ LESS_OPERATORS.include?(operator) ? 'max' : 'min'
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -58,7 +58,7 @@ module RuboCop
58
58
  private
59
59
 
60
60
  def message(range)
61
- format(MSG, char: range.source.chars.last)
61
+ format(MSG, char: range.source[-1])
62
62
  end
63
63
 
64
64
  def str_contents_range(node)
@@ -76,6 +76,7 @@ module RuboCop
76
76
  node.loc.to_hash.key?(:begin) && !node.loc.begin.nil?
77
77
  end
78
78
 
79
+ # rubocop:disable Metrics/CyclomaticComplexity
79
80
  def allowed_escape?(node, range)
80
81
  escaped = range.source[(1..-1)]
81
82
 
@@ -88,13 +89,14 @@ module RuboCop
88
89
  # with different versions of Ruby so that e.g. /\d/ != /d/
89
90
  return true if /[\n\\[[:alnum:]]]/.match?(escaped[0])
90
91
 
91
- return true if escaped[0] == ' ' && percent_array_literal?(node)
92
+ return true if escaped[0] == ' ' && (percent_array_literal?(node) || node.heredoc?)
92
93
 
93
94
  return true if disabling_interpolation?(range)
94
95
  return true if delimiter?(node, escaped[0])
95
96
 
96
97
  false
97
98
  end
99
+ # rubocop:enable Metrics/CyclomaticComplexity
98
100
 
99
101
  def interpolation_not_enabled?(node)
100
102
  single_quoted?(node) ||
@@ -169,9 +171,10 @@ module RuboCop
169
171
  # Allow \#{foo}, \#$foo, \#@foo, and \#@@foo
170
172
  # for escaping local, global, instance and class variable interpolations
171
173
  return true if range.source.match?(/\A\\#[{$@]/)
172
-
173
174
  # Also allow #\{foo}, #\$foo, #\@foo and #\@@foo
174
175
  return true if range.adjust(begin_pos: -2).source.match?(/\A[^\\]#\\[{$@]/)
176
+ # For `\#\{foo} allow `\#` and warn `\{`
177
+ return true if range.adjust(end_pos: 1).source == '\\#\\{'
175
178
 
176
179
  false
177
180
  end
@@ -81,7 +81,7 @@ module RuboCop
81
81
  PATTERN
82
82
 
83
83
  def on_send(node)
84
- return unless node.arguments?
84
+ return unless node.parent && node.arguments?
85
85
  return if not_modifier_form?(node.parent)
86
86
 
87
87
  previous_older_sibling = find_previous_older_sibling(node)
@@ -102,8 +102,10 @@ module RuboCop
102
102
  node.if_type? && !node.modifier_form?
103
103
  end
104
104
 
105
- def find_previous_older_sibling(node) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity
105
+ def find_previous_older_sibling(node) # rubocop:disable Metrics
106
106
  search_node(node).left_siblings.reverse.find do |sibling|
107
+ next unless sibling.is_a?(AST::Node)
108
+
107
109
  sibling = sibling_node(sibling)
108
110
  break unless sibling&.send_type? && sibling&.method?(node.method_name)
109
111
  break unless sibling.arguments? && !sibling.receiver
@@ -86,12 +86,12 @@ module RuboCop
86
86
 
87
87
  def on_send(node)
88
88
  return unless (block_node = node.block_node)
89
- return if block_node.body.begin_type?
89
+ return if block_node.body&.begin_type?
90
90
  return if receiver_allowed?(block_node.receiver)
91
91
  return unless (regexp_method_send_node = extract_send_node(block_node))
92
92
  return if match_predicate_without_receiver?(regexp_method_send_node)
93
93
 
94
- opposite = regexp_method_send_node.send_type? && regexp_method_send_node.method?(:!~)
94
+ opposite = opposite?(regexp_method_send_node)
95
95
  regexp = find_regexp(regexp_method_send_node, block_node)
96
96
 
97
97
  register_offense(node, block_node, regexp, opposite)
@@ -128,6 +128,10 @@ module RuboCop
128
128
  regexp_method_send_node
129
129
  end
130
130
 
131
+ def opposite?(regexp_method_send_node)
132
+ regexp_method_send_node.send_type? && regexp_method_send_node.method?(:!~)
133
+ end
134
+
131
135
  def find_regexp(node, block)
132
136
  return node.child_nodes.first if node.match_with_lvasgn_type?
133
137
 
@@ -119,11 +119,6 @@ module RuboCop
119
119
  # @!method custom_fail_methods(node)
120
120
  def_node_search :custom_fail_methods, '{(def :fail ...) (defs _ :fail ...)}'
121
121
 
122
- def on_new_investigation
123
- ast = processed_source.ast
124
- @custom_fail_defined = ast && custom_fail_methods(ast).any?
125
- end
126
-
127
122
  def on_rescue(node)
128
123
  return unless style == :semantic
129
124
 
@@ -141,7 +136,7 @@ module RuboCop
141
136
  when :semantic
142
137
  check_send(:raise, node) unless ignored_node?(node)
143
138
  when :only_raise
144
- return if @custom_fail_defined
139
+ return if custom_fail_defined?
145
140
 
146
141
  check_send(:fail, node)
147
142
  when :only_fail
@@ -151,6 +146,13 @@ module RuboCop
151
146
 
152
147
  private
153
148
 
149
+ def custom_fail_defined?
150
+ return @custom_fail_defined if defined?(@custom_fail_defined)
151
+
152
+ ast = processed_source.ast
153
+ @custom_fail_defined = ast && custom_fail_methods(ast).any?
154
+ end
155
+
154
156
  def message(method_name)
155
157
  case style
156
158
  when :semantic
@@ -88,6 +88,10 @@ module RuboCop
88
88
  include TrailingComma
89
89
  extend AutoCorrector
90
90
 
91
+ def self.autocorrect_incompatible_with
92
+ [Layout::HeredocArgumentClosingParenthesis]
93
+ end
94
+
91
95
  def on_send(node)
92
96
  return unless node.arguments? && node.parenthesized?
93
97
 
@@ -96,10 +100,6 @@ module RuboCop
96
100
  node.source_range.end_pos)
97
101
  end
98
102
  alias on_csend on_send
99
-
100
- def self.autocorrect_incompatible_with
101
- [Layout::HeredocArgumentClosingParenthesis]
102
- end
103
103
  end
104
104
  end
105
105
  end
@@ -27,6 +27,25 @@ module RuboCop
27
27
  # # bad (contains spaces)
28
28
  # %w[foo\ bar baz\ quux]
29
29
  #
30
+ # # bad
31
+ # [
32
+ # ['one', 'One'],
33
+ # ['two', 'Two']
34
+ # ]
35
+ #
36
+ # # good
37
+ # [
38
+ # %w[one One],
39
+ # %w[two Two]
40
+ # ]
41
+ #
42
+ # # good (2d array containing spaces)
43
+ # [
44
+ # ['one', 'One'],
45
+ # ['two', 'Two'],
46
+ # ['forty two', 'Forty Two']
47
+ # ]
48
+ #
30
49
  # @example EnforcedStyle: brackets
31
50
  # # good
32
51
  # ['foo', 'bar', 'baz']
@@ -36,6 +55,19 @@ module RuboCop
36
55
  #
37
56
  # # good (contains spaces)
38
57
  # ['foo bar', 'baz quux']
58
+ #
59
+ # # good
60
+ # [
61
+ # ['one', 'One'],
62
+ # ['two', 'Two']
63
+ # ]
64
+ #
65
+ # # bad
66
+ # [
67
+ # %w[one One],
68
+ # %w[two Two]
69
+ # ]
70
+ #
39
71
  class WordArray < Base
40
72
  include ArrayMinSize
41
73
  include ArraySyntax
@@ -53,6 +85,7 @@ module RuboCop
53
85
  def on_array(node)
54
86
  if bracketed_array_of?(:str, node)
55
87
  return if complex_content?(node.values)
88
+ return if within_2d_array_of_complex_content?(node)
56
89
 
57
90
  check_bracketed_array(node, 'w')
58
91
  elsif node.percent_literal?(:string)
@@ -62,6 +95,14 @@ module RuboCop
62
95
 
63
96
  private
64
97
 
98
+ def within_2d_array_of_complex_content?(node)
99
+ return false unless (parent = node.parent)
100
+
101
+ parent.array_type? &&
102
+ parent.values.all?(&:array_type?) &&
103
+ parent.values.any? { |subarray| complex_content?(subarray.values) }
104
+ end
105
+
65
106
  def complex_content?(strings, complex_regex: word_regex)
66
107
  strings.any? do |s|
67
108
  next unless s.str_content
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Forbids Yoda expressions, i.e. binary operations (using `*`, `+`, `&`, `|`,
7
+ # and `^` operators) where the order of expression is reversed, eg. `1 + x`.
8
+ # This cop complements `Style/YodaCondition` cop, which has a similar purpose.
9
+ #
10
+ # @safety
11
+ # This cop is unsafe because binary operators can be defined
12
+ # differently on different classes, and are not guaranteed to
13
+ # have the same result if reversed.
14
+ #
15
+ # @example SupportedOperators: ['*', '+', '&'']
16
+ # # bad
17
+ # 1 + x
18
+ # 10 * y
19
+ # 1 & z
20
+ #
21
+ # # good
22
+ # 60 * 24
23
+ # x + 1
24
+ # y * 10
25
+ # z & 1
26
+ #
27
+ # # good
28
+ # 1 | x
29
+ #
30
+ class YodaExpression < Base
31
+ extend AutoCorrector
32
+
33
+ MSG = 'Non-literal operand (`%<source>s`) should be first.'
34
+
35
+ RESTRICT_ON_SEND = %i[* + & | ^].freeze
36
+
37
+ def on_new_investigation
38
+ @offended_nodes = nil
39
+ end
40
+
41
+ def on_send(node)
42
+ return unless supported_operators.include?(node.method_name.to_s)
43
+
44
+ lhs = node.receiver
45
+ rhs = node.first_argument
46
+ return if !lhs.numeric_type? || rhs.numeric_type?
47
+
48
+ return if offended_ancestor?(node)
49
+
50
+ message = format(MSG, source: rhs.source)
51
+ add_offense(node, message: message) do |corrector|
52
+ corrector.swap(lhs, rhs)
53
+ end
54
+
55
+ offended_nodes.add(node)
56
+ end
57
+
58
+ private
59
+
60
+ def supported_operators
61
+ Array(cop_config['SupportedOperators'])
62
+ end
63
+
64
+ def offended_ancestor?(node)
65
+ node.each_ancestor(:send).any? { |ancestor| @offended_nodes&.include?(ancestor) }
66
+ end
67
+
68
+ def offended_nodes
69
+ @offended_nodes ||= Set.new.compare_by_identity
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -34,43 +34,55 @@ module RuboCop
34
34
  class ZeroLengthPredicate < Base
35
35
  extend AutoCorrector
36
36
 
37
- ZERO_MSG = 'Use `empty?` instead of `%<lhs>s %<opr>s %<rhs>s`.'
38
- NONZERO_MSG = 'Use `!empty?` instead of `%<lhs>s %<opr>s %<rhs>s`.'
37
+ ZERO_MSG = 'Use `empty?` instead of `%<current>s`.'
38
+ NONZERO_MSG = 'Use `!empty?` instead of `%<current>s`.'
39
39
 
40
40
  RESTRICT_ON_SEND = %i[size length].freeze
41
41
 
42
42
  def on_send(node)
43
43
  check_zero_length_predicate(node)
44
- check_nonzero_length_predicate(node)
44
+ check_zero_length_comparison(node)
45
+ check_nonzero_length_comparison(node)
45
46
  end
46
47
 
47
48
  private
48
49
 
49
50
  def check_zero_length_predicate(node)
50
- zero_length_predicate = zero_length_predicate(node.parent)
51
- return unless zero_length_predicate
51
+ return unless (length_method = zero_length_predicate(node.parent))
52
52
 
53
- lhs, opr, rhs = zero_length_predicate
53
+ offense = node.loc.selector.join(node.parent.source_range.end)
54
+ message = format(ZERO_MSG, current: "#{length_method}.zero?")
55
+
56
+ add_offense(offense, message: message) do |corrector|
57
+ corrector.replace(offense, 'empty?')
58
+ end
59
+ end
60
+
61
+ def check_zero_length_comparison(node)
62
+ zero_length_comparison = zero_length_comparison(node.parent)
63
+ return unless zero_length_comparison
64
+
65
+ lhs, opr, rhs = zero_length_comparison
54
66
 
55
67
  return if non_polymorphic_collection?(node.parent)
56
68
 
57
69
  add_offense(
58
- node.parent, message: format(ZERO_MSG, lhs: lhs, opr: opr, rhs: rhs)
70
+ node.parent, message: format(ZERO_MSG, current: "#{lhs} #{opr} #{rhs}")
59
71
  ) do |corrector|
60
72
  corrector.replace(node.parent, replacement(node.parent))
61
73
  end
62
74
  end
63
75
 
64
- def check_nonzero_length_predicate(node)
65
- nonzero_length_predicate = nonzero_length_predicate(node.parent)
66
- return unless nonzero_length_predicate
76
+ def check_nonzero_length_comparison(node)
77
+ nonzero_length_comparison = nonzero_length_comparison(node.parent)
78
+ return unless nonzero_length_comparison
67
79
 
68
- lhs, opr, rhs = nonzero_length_predicate
80
+ lhs, opr, rhs = nonzero_length_comparison
69
81
 
70
82
  return if non_polymorphic_collection?(node.parent)
71
83
 
72
84
  add_offense(
73
- node.parent, message: format(NONZERO_MSG, lhs: lhs, opr: opr, rhs: rhs)
85
+ node.parent, message: format(NONZERO_MSG, current: "#{lhs} #{opr} #{rhs}")
74
86
  ) do |corrector|
75
87
  corrector.replace(node.parent, replacement(node.parent))
76
88
  end
@@ -78,14 +90,19 @@ module RuboCop
78
90
 
79
91
  # @!method zero_length_predicate(node)
80
92
  def_node_matcher :zero_length_predicate, <<~PATTERN
93
+ (send (send (...) ${:length :size}) :zero?)
94
+ PATTERN
95
+
96
+ # @!method zero_length_comparison(node)
97
+ def_node_matcher :zero_length_comparison, <<~PATTERN
81
98
  {(send (send (...) ${:length :size}) $:== (int $0))
82
99
  (send (int $0) $:== (send (...) ${:length :size}))
83
100
  (send (send (...) ${:length :size}) $:< (int $1))
84
101
  (send (int $1) $:> (send (...) ${:length :size}))}
85
102
  PATTERN
86
103
 
87
- # @!method nonzero_length_predicate(node)
88
- def_node_matcher :nonzero_length_predicate, <<~PATTERN
104
+ # @!method nonzero_length_comparison(node)
105
+ def_node_matcher :nonzero_length_comparison, <<~PATTERN
89
106
  {(send (send (...) ${:length :size}) ${:> :!=} (int $0))
90
107
  (send (int $0) ${:< :!=} (send (...) ${:length :size}))}
91
108
  PATTERN
@@ -10,20 +10,6 @@ module RuboCop
10
10
  # first the ones needed for autocorrection (if any), then the rest
11
11
  # (unless autocorrections happened).
12
12
  class Team
13
- attr_reader :errors, :warnings, :updated_source_file, :cops
14
-
15
- alias updated_source_file? updated_source_file
16
-
17
- def initialize(cops, config = nil, options = {})
18
- @cops = cops
19
- @config = config
20
- @options = options
21
- reset
22
- @ready = true
23
-
24
- validate_config
25
- end
26
-
27
13
  # @return [Team]
28
14
  def self.new(cop_or_classes, config, options = {})
29
15
  # Support v0 api:
@@ -47,6 +33,35 @@ module RuboCop
47
33
  end
48
34
  end
49
35
 
36
+ # @return [Array<Force>] needed for the given cops
37
+ def self.forces_for(cops)
38
+ needed = Hash.new { |h, k| h[k] = [] }
39
+ cops.each do |cop|
40
+ forces = cop.class.joining_forces
41
+ if forces.is_a?(Array)
42
+ forces.each { |force| needed[force] << cop }
43
+ elsif forces
44
+ needed[forces] << cop
45
+ end
46
+ end
47
+
48
+ needed.map { |force_class, joining_cops| force_class.new(joining_cops) }
49
+ end
50
+
51
+ attr_reader :errors, :warnings, :updated_source_file, :cops
52
+
53
+ alias updated_source_file? updated_source_file
54
+
55
+ def initialize(cops, config = nil, options = {})
56
+ @cops = cops
57
+ @config = config
58
+ @options = options
59
+ reset
60
+ @ready = true
61
+
62
+ validate_config
63
+ end
64
+
50
65
  def autocorrect?
51
66
  @options[:autocorrect]
52
67
  end
@@ -94,21 +109,6 @@ module RuboCop
94
109
  @forces ||= self.class.forces_for(cops)
95
110
  end
96
111
 
97
- # @return [Array<Force>] needed for the given cops
98
- def self.forces_for(cops)
99
- needed = Hash.new { |h, k| h[k] = [] }
100
- cops.each do |cop|
101
- forces = cop.class.joining_forces
102
- if forces.is_a?(Array)
103
- forces.each { |force| needed[force] << cop }
104
- elsif forces
105
- needed[forces] << cop
106
- end
107
- end
108
-
109
- needed.map { |force_class, joining_cops| force_class.new(joining_cops) }
110
- end
111
-
112
112
  def external_dependency_checksum
113
113
  keys = cops.map(&:external_dependency_checksum).compact
114
114
  Digest::SHA1.hexdigest(keys.join)
@@ -108,7 +108,6 @@ module RuboCop
108
108
  :skip_children
109
109
  end
110
110
 
111
- # rubocop:disable Layout/ClassStructure
112
111
  NODE_HANDLER_METHOD_NAMES = [
113
112
  [VARIABLE_ASSIGNMENT_TYPE, :process_variable_assignment],
114
113
  [REGEXP_NAMED_CAPTURE_TYPE, :process_regexp_named_captures],
@@ -123,8 +122,6 @@ module RuboCop
123
122
  *SCOPE_TYPES.product([:process_scope])
124
123
  ].to_h.freeze
125
124
  private_constant :NODE_HANDLER_METHOD_NAMES
126
- # rubocop:enable Layout/ClassStructure
127
-
128
125
  def node_handler_method_name(node)
129
126
  NODE_HANDLER_METHOD_NAMES[node.type]
130
127
  end
@@ -33,7 +33,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
33
33
  cops.with_department(department).sort!
34
34
  end
35
35
 
36
- def cops_body(cop, description, examples_objects, safety_objects, pars) # rubocop:disable Metrics/AbcSize
36
+ def cops_body(cop, description, examples_objects, safety_objects, see_objects, pars) # rubocop:disable Metrics/AbcSize, Metrics/ParameterLists
37
37
  check_examples_to_have_the_default_enforced_style!(examples_objects, cop)
38
38
 
39
39
  content = h2(cop.cop_name)
@@ -43,7 +43,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
43
43
  content << safety_object(safety_objects) if safety_objects.any? { |s| !s.text.blank? }
44
44
  content << examples(examples_objects) if examples_objects.any?
45
45
  content << configurations(cop.department, pars)
46
- content << references(cop)
46
+ content << references(cop, see_objects)
47
47
  content
48
48
  end
49
49
 
@@ -224,14 +224,16 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
224
224
  end
225
225
  end
226
226
 
227
- def references(cop)
227
+ def references(cop, see_objects) # rubocop:disable Metrics/AbcSize
228
228
  cop_config = config.for_cop(cop)
229
229
  urls = RuboCop::Cop::MessageAnnotator.new(config, cop.name, cop_config, {}).urls
230
- return '' if urls.empty?
230
+ return '' if urls.empty? && see_objects.empty?
231
231
 
232
232
  content = h3('References')
233
233
  content << urls.map { |url| "* #{url}" }.join("\n")
234
- content << "\n"
234
+ content << "\n" unless urls.empty?
235
+ content << see_objects.map { |see| "* #{see.name}" }.join("\n")
236
+ content << "\n" unless see_objects.empty?
235
237
  content
236
238
  end
237
239
 
@@ -257,7 +259,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
257
259
  end
258
260
  end
259
261
 
260
- def print_cop_with_doc(cop)
262
+ def print_cop_with_doc(cop) # rubocop:todo Metrics/AbcSize, Metrics/MethodLength
261
263
  cop_config = config.for_cop(cop)
262
264
  non_display_keys = %w[
263
265
  Description Enabled StyleGuide Reference Safe SafeAutoCorrect VersionAdded
@@ -265,13 +267,14 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
265
267
  ]
266
268
  pars = cop_config.reject { |k| non_display_keys.include? k }
267
269
  description = 'No documentation'
268
- examples_object = safety_object = []
270
+ examples_object = safety_object = see_object = []
269
271
  cop_code(cop) do |code_object|
270
272
  description = code_object.docstring unless code_object.docstring.blank?
271
273
  examples_object = code_object.tags('example')
272
274
  safety_object = code_object.tags('safety')
275
+ see_object = code_object.tags('see')
273
276
  end
274
- cops_body(cop, description, examples_object, safety_object, pars)
277
+ cops_body(cop, description, examples_object, safety_object, see_object, pars)
275
278
  end
276
279
 
277
280
  def cop_code(cop)
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RuboCop
4
+ # The bootstrap module for formatter.
5
+ # @api private
4
6
  module Formatter
5
7
  require_relative 'formatter/text_util'
6
8
 
@@ -46,7 +46,7 @@ module RuboCop
46
46
  matches =
47
47
  if pattern == path
48
48
  true
49
- elsif pattern.match?(/[*{\[?]/)
49
+ elsif glob?(pattern)
50
50
  File.fnmatch?(pattern, path, File::FNM_PATHNAME | File::FNM_EXTGLOB)
51
51
  end
52
52
 
@@ -68,6 +68,11 @@ module RuboCop
68
68
  %r{\A([A-Z]:)?/}i.match?(path)
69
69
  end
70
70
 
71
+ # Returns true for a glob
72
+ def glob?(path)
73
+ path.match?(/[*{\[?]/)
74
+ end
75
+
71
76
  def hidden_file_in_not_hidden_dir?(pattern, path)
72
77
  hidden_file?(path) &&
73
78
  File.fnmatch?(
@@ -53,7 +53,7 @@ module RuboCop
53
53
  def remove_oldest_files(files, dirs, cache_root, verbose)
54
54
  # Add 1 to half the number of files, so that we remove the file if
55
55
  # there's only 1 left.
56
- remove_count = 1 + (files.length / 2)
56
+ remove_count = (files.length / 2) + 1
57
57
  puts "Removing the #{remove_count} oldest files from #{cache_root}" if verbose
58
58
  sorted = files.sort_by { |path| File.mtime(path) }
59
59
  remove_files(sorted, dirs, remove_count)