rubocop 1.40.0 → 1.43.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +2 -2
  4. data/config/default.yml +52 -2
  5. data/lib/rubocop/cli.rb +1 -1
  6. data/lib/rubocop/config.rb +34 -11
  7. data/lib/rubocop/config_loader.rb +9 -0
  8. data/lib/rubocop/config_loader_resolver.rb +5 -1
  9. data/lib/rubocop/config_validator.rb +1 -1
  10. data/lib/rubocop/cop/badge.rb +9 -4
  11. data/lib/rubocop/cop/base.rb +83 -66
  12. data/lib/rubocop/cop/commissioner.rb +8 -3
  13. data/lib/rubocop/cop/cop.rb +29 -29
  14. data/lib/rubocop/cop/corrector.rb +23 -11
  15. data/lib/rubocop/cop/gemspec/dependency_version.rb +16 -18
  16. data/lib/rubocop/cop/internal_affairs/cop_description.rb +3 -1
  17. data/lib/rubocop/cop/layout/class_structure.rb +32 -11
  18. data/lib/rubocop/cop/layout/comment_indentation.rb +3 -1
  19. data/lib/rubocop/cop/layout/empty_lines.rb +2 -0
  20. data/lib/rubocop/cop/layout/extra_spacing.rb +10 -6
  21. data/lib/rubocop/cop/layout/first_array_element_line_break.rb +38 -2
  22. data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +49 -2
  23. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +61 -2
  24. data/lib/rubocop/cop/layout/first_method_parameter_line_break.rb +52 -2
  25. data/lib/rubocop/cop/layout/indentation_style.rb +7 -2
  26. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +5 -0
  27. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +11 -5
  28. data/lib/rubocop/cop/layout/line_length.rb +2 -0
  29. data/lib/rubocop/cop/layout/multiline_array_line_breaks.rb +51 -2
  30. data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -1
  31. data/lib/rubocop/cop/layout/multiline_hash_key_line_breaks.rb +49 -2
  32. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +53 -2
  33. data/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb +58 -2
  34. data/lib/rubocop/cop/layout/redundant_line_break.rb +2 -2
  35. data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
  36. data/lib/rubocop/cop/layout/trailing_empty_lines.rb +1 -1
  37. data/lib/rubocop/cop/layout/trailing_whitespace.rb +11 -4
  38. data/lib/rubocop/cop/lint/constant_resolution.rb +4 -0
  39. data/lib/rubocop/cop/lint/debugger.rb +3 -1
  40. data/lib/rubocop/cop/lint/duplicate_branch.rb +0 -2
  41. data/lib/rubocop/cop/lint/duplicate_methods.rb +19 -8
  42. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +10 -5
  43. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +19 -0
  44. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +3 -1
  45. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +1 -1
  46. data/lib/rubocop/cop/lint/regexp_as_condition.rb +6 -0
  47. data/lib/rubocop/cop/lint/require_parentheses.rb +3 -1
  48. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +8 -19
  49. data/lib/rubocop/cop/lint/unused_method_argument.rb +2 -1
  50. data/lib/rubocop/cop/lint/useless_rescue.rb +71 -0
  51. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +5 -3
  52. data/lib/rubocop/cop/metrics/class_length.rb +1 -1
  53. data/lib/rubocop/cop/metrics/module_length.rb +1 -1
  54. data/lib/rubocop/cop/metrics/parameter_lists.rb +27 -0
  55. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
  56. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +4 -4
  57. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +2 -2
  58. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  59. data/lib/rubocop/cop/mixin/allowed_identifiers.rb +2 -2
  60. data/lib/rubocop/cop/mixin/annotation_comment.rb +13 -6
  61. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +21 -9
  62. data/lib/rubocop/cop/mixin/first_element_line_break.rb +11 -7
  63. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +14 -2
  64. data/lib/rubocop/cop/mixin/line_length_help.rb +8 -1
  65. data/lib/rubocop/cop/mixin/method_complexity.rb +5 -3
  66. data/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb +5 -3
  67. data/lib/rubocop/cop/mixin/percent_array.rb +3 -5
  68. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +1 -1
  69. data/lib/rubocop/cop/mixin/require_library.rb +2 -0
  70. data/lib/rubocop/cop/mixin/rescue_node.rb +3 -3
  71. data/lib/rubocop/cop/mixin/statement_modifier.rb +2 -1
  72. data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
  73. data/lib/rubocop/cop/naming/class_and_module_camel_case.rb +2 -0
  74. data/lib/rubocop/cop/naming/inclusive_language.rb +4 -1
  75. data/lib/rubocop/cop/registry.rb +28 -25
  76. data/lib/rubocop/cop/security/compound_hash.rb +2 -1
  77. data/lib/rubocop/cop/style/alias.rb +9 -1
  78. data/lib/rubocop/cop/style/block_comments.rb +1 -1
  79. data/lib/rubocop/cop/style/concat_array_literals.rb +86 -0
  80. data/lib/rubocop/cop/style/documentation.rb +11 -5
  81. data/lib/rubocop/cop/style/guard_clause.rb +17 -9
  82. data/lib/rubocop/cop/style/hash_each_methods.rb +13 -1
  83. data/lib/rubocop/cop/style/hash_syntax.rb +11 -7
  84. data/lib/rubocop/cop/style/identical_conditional_branches.rb +15 -0
  85. data/lib/rubocop/cop/style/if_with_semicolon.rb +2 -3
  86. data/lib/rubocop/cop/style/inverse_methods.rb +2 -0
  87. data/lib/rubocop/cop/style/line_end_concatenation.rb +4 -1
  88. data/lib/rubocop/cop/style/map_to_set.rb +61 -0
  89. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +12 -9
  90. data/lib/rubocop/cop/style/method_def_parentheses.rb +11 -4
  91. data/lib/rubocop/cop/style/min_max_comparison.rb +73 -0
  92. data/lib/rubocop/cop/style/missing_else.rb +13 -1
  93. data/lib/rubocop/cop/style/operator_method_call.rb +15 -1
  94. data/lib/rubocop/cop/style/redundant_constant_base.rb +13 -0
  95. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +39 -0
  96. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +2 -1
  97. data/lib/rubocop/cop/style/redundant_string_escape.rb +6 -3
  98. data/lib/rubocop/cop/style/require_order.rb +63 -9
  99. data/lib/rubocop/cop/style/select_by_regexp.rb +6 -2
  100. data/lib/rubocop/cop/style/semicolon.rb +2 -1
  101. data/lib/rubocop/cop/style/signal_exception.rb +8 -6
  102. data/lib/rubocop/cop/style/string_hash_keys.rb +4 -1
  103. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +4 -4
  104. data/lib/rubocop/cop/style/word_array.rb +41 -0
  105. data/lib/rubocop/cop/style/yoda_expression.rb +81 -0
  106. data/lib/rubocop/cop/style/zero_length_predicate.rb +31 -14
  107. data/lib/rubocop/cop/team.rb +29 -29
  108. data/lib/rubocop/cop/util.rb +31 -4
  109. data/lib/rubocop/cop/variable_force.rb +0 -3
  110. data/lib/rubocop/cops_documentation_generator.rb +33 -11
  111. data/lib/rubocop/directive_comment.rb +1 -1
  112. data/lib/rubocop/file_patterns.rb +43 -0
  113. data/lib/rubocop/formatter.rb +2 -0
  114. data/lib/rubocop/options.rb +1 -1
  115. data/lib/rubocop/path_util.rb +38 -22
  116. data/lib/rubocop/result_cache.rb +1 -1
  117. data/lib/rubocop/runner.rb +10 -3
  118. data/lib/rubocop/target_finder.rb +1 -1
  119. data/lib/rubocop/target_ruby.rb +0 -1
  120. data/lib/rubocop/version.rb +1 -1
  121. data/lib/rubocop.rb +7 -1
  122. metadata +16 -10
  123. data/lib/rubocop/optimized_patterns.rb +0 -38
@@ -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)
@@ -52,6 +52,21 @@ module RuboCop
52
52
  end
53
53
  # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
54
54
 
55
+ def any_descendant?(node, *types)
56
+ if block_given?
57
+ node.each_descendant(*types) do |descendant|
58
+ return true if yield(descendant)
59
+ end
60
+ else
61
+ # Use a block version to avoid allocating enumerators.
62
+ node.each_descendant do # rubocop:disable Lint/UnreachableLoop
63
+ return true
64
+ end
65
+ end
66
+
67
+ false
68
+ end
69
+
55
70
  def args_begin(node)
56
71
  loc = node.loc
57
72
  selector = if node.super_type? || node.yield_type?
@@ -71,14 +86,19 @@ module RuboCop
71
86
  def on_node(syms, sexp, excludes = [], &block)
72
87
  return to_enum(:on_node, syms, sexp, excludes) unless block
73
88
 
74
- yield sexp if Array(syms).include?(sexp.type)
75
- return if Array(excludes).include?(sexp.type)
89
+ yield sexp if include_or_equal?(syms, sexp.type)
90
+ return if include_or_equal?(excludes, sexp.type)
76
91
 
77
92
  sexp.each_child_node { |elem| on_node(syms, elem, excludes, &block) }
78
93
  end
79
94
 
95
+ LINE_BEGINS_REGEX_CACHE = Hash.new do |hash, index|
96
+ hash[index] = /^\s{#{index}}\S/
97
+ end
98
+ private_constant :LINE_BEGINS_REGEX_CACHE
99
+
80
100
  def begins_its_line?(range)
81
- range.source_line.index(/\S/) == range.column
101
+ range.source_line.match?(LINE_BEGINS_REGEX_CACHE[range.column])
82
102
  end
83
103
 
84
104
  # Returns, for example, a bare `if` node if the given node is an `if`
@@ -152,8 +172,11 @@ module RuboCop
152
172
  ' ' * (node.loc.column + offset)
153
173
  end
154
174
 
175
+ @to_supported_styles_cache = {}
176
+
155
177
  def to_supported_styles(enforced_style)
156
- enforced_style.sub(/^Enforced/, 'Supported').sub('Style', 'Styles')
178
+ @to_supported_styles_cache[enforced_style] ||=
179
+ enforced_style.sub(/^Enforced/, 'Supported').sub('Style', 'Styles')
157
180
  end
158
181
 
159
182
  private
@@ -162,6 +185,10 @@ module RuboCop
162
185
  src = src.dup if RUBY_ENGINE == 'jruby'
163
186
  src.force_encoding(Encoding.default_external).valid_encoding?
164
187
  end
188
+
189
+ def include_or_equal?(source, target)
190
+ source == target || (source.is_a?(Array) && source.include?(target))
191
+ end
165
192
  end
166
193
  end
167
194
  end
@@ -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)
@@ -42,8 +42,8 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
42
42
  content << "#{description}\n"
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
- content << configurations(pars)
46
- content << references(cop)
45
+ content << configurations(cop.department, pars)
46
+ content << references(cop, see_objects)
47
47
  content
48
48
  end
49
49
 
@@ -136,7 +136,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
136
136
  content
137
137
  end
138
138
 
139
- def configurations(pars)
139
+ def configurations(department, pars)
140
140
  return '' if pars.empty?
141
141
 
142
142
  header = ['Name', 'Default value', 'Configurable values']
@@ -147,12 +147,20 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
147
147
  content = configs.map do |name|
148
148
  configurable = configurable_values(pars, name)
149
149
  default = format_table_value(pars[name])
150
- [name, default, configurable]
150
+
151
+ [configuration_name(department, name), default, configurable]
151
152
  end
152
153
 
153
154
  h3('Configurable attributes') + to_table(header, content)
154
155
  end
155
156
 
157
+ def configuration_name(department, name)
158
+ return name unless name == 'AllowMultilineFinalElement'
159
+
160
+ filename = "#{department_to_basename(department)}.adoc"
161
+ "xref:#{filename}#allowmultilinefinalelement[AllowMultilineFinalElement]"
162
+ end
163
+
156
164
  # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength
157
165
  def configurable_values(pars, name)
158
166
  case name
@@ -216,21 +224,34 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
216
224
  end
217
225
  end
218
226
 
219
- def references(cop)
227
+ def references(cop, see_objects) # rubocop:disable Metrics/AbcSize
220
228
  cop_config = config.for_cop(cop)
221
229
  urls = RuboCop::Cop::MessageAnnotator.new(config, cop.name, cop_config, {}).urls
222
- return '' if urls.empty?
230
+ return '' if urls.empty? && see_objects.empty?
223
231
 
224
232
  content = h3('References')
225
233
  content << urls.map { |url| "* #{url}" }.join("\n")
226
- 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?
227
237
  content
228
238
  end
229
239
 
240
+ def footer_for_department(department)
241
+ return '' unless department == :Layout
242
+
243
+ filename = "#{department_to_basename(department)}_footer.adoc"
244
+ file = "#{Dir.pwd}/docs/modules/ROOT/partials/#{filename}"
245
+ return '' unless File.exist?(file)
246
+
247
+ "\ninclude::../partials/#{filename}[]\n"
248
+ end
249
+
230
250
  def print_cops_of_department(department)
231
251
  selected_cops = cops_of_department(department)
232
252
  content = +"= #{department}\n"
233
253
  selected_cops.each { |cop| content << print_cop_with_doc(cop) }
254
+ content << footer_for_department(department)
234
255
  file_name = "#{Dir.pwd}/docs/modules/ROOT/pages/#{department_to_basename(department)}.adoc"
235
256
  File.open(file_name, 'w') do |file|
236
257
  puts "* generated #{file_name}"
@@ -238,7 +259,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
238
259
  end
239
260
  end
240
261
 
241
- def print_cop_with_doc(cop)
262
+ def print_cop_with_doc(cop) # rubocop:todo Metrics/AbcSize, Metrics/MethodLength
242
263
  cop_config = config.for_cop(cop)
243
264
  non_display_keys = %w[
244
265
  Description Enabled StyleGuide Reference Safe SafeAutoCorrect VersionAdded
@@ -246,13 +267,14 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
246
267
  ]
247
268
  pars = cop_config.reject { |k| non_display_keys.include? k }
248
269
  description = 'No documentation'
249
- examples_object = safety_object = []
270
+ examples_object = safety_object = see_object = []
250
271
  cop_code(cop) do |code_object|
251
272
  description = code_object.docstring unless code_object.docstring.blank?
252
273
  examples_object = code_object.tags('example')
253
274
  safety_object = code_object.tags('safety')
275
+ see_object = code_object.tags('see')
254
276
  end
255
- cops_body(cop, description, examples_object, safety_object, pars)
277
+ cops_body(cop, description, examples_object, safety_object, see_object, pars)
256
278
  end
257
279
 
258
280
  def cop_code(cop)
@@ -35,7 +35,7 @@ module RuboCop
35
35
 
36
36
  # Checks if this directive relates to single line
37
37
  def single_line?
38
- !self.class.before_comment(comment.text).empty?
38
+ !comment.text.start_with?(DIRECTIVE_COMMENT_REGEXP)
39
39
  end
40
40
 
41
41
  # Checks if this directive contains all the given cop names
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ # A wrapper around patterns array to perform optimized search.
5
+ #
6
+ # For projects with a large set of rubocop todo files, most items in `Exclude`/`Include`
7
+ # are exact file names. It is wasteful to linearly check the list of patterns over and over
8
+ # to check if the file is relevant to the cop.
9
+ #
10
+ # This class partitions an array of patterns into a set of exact match strings and the rest
11
+ # of the patterns. This way we can firstly do a cheap check in the set and then proceed via
12
+ # the costly patterns check, if needed.
13
+ # @api private
14
+ class FilePatterns
15
+ @cache = {}.compare_by_identity
16
+
17
+ def self.from(patterns)
18
+ @cache[patterns] ||= new(patterns)
19
+ end
20
+
21
+ def initialize(patterns)
22
+ @strings = Set.new
23
+ @patterns = []
24
+ partition_patterns(patterns)
25
+ end
26
+
27
+ def match?(path)
28
+ @strings.include?(path) || @patterns.any? { |pattern| PathUtil.match_path?(pattern, path) }
29
+ end
30
+
31
+ private
32
+
33
+ def partition_patterns(patterns)
34
+ patterns.each do |pattern|
35
+ if pattern.is_a?(String) && !pattern.match?(/[*{\[?]/)
36
+ @strings << pattern
37
+ else
38
+ @patterns << pattern
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -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
 
@@ -465,7 +465,7 @@ module RuboCop
465
465
 
466
466
  # This module contains help texts for command line options.
467
467
  # @api private
468
- module OptionsHelp # rubocop:disable Metrics/ModuleLength
468
+ module OptionsHelp
469
469
  MAX_EXCL = RuboCop::Options::DEFAULT_MAXIMUM_EXCLUSION_ITEMS.to_s
470
470
  FORMATTER_OPTION_LIST = RuboCop::Formatter::FormatterSet::BUILTIN_FORMATTERS_FOR_KEYS.keys
471
471
 
@@ -3,33 +3,44 @@
3
3
  module RuboCop
4
4
  # Common methods and behaviors for dealing with paths.
5
5
  module PathUtil
6
+ class << self
7
+ attr_accessor :relative_paths_cache
8
+ end
9
+ self.relative_paths_cache = Hash.new { |hash, key| hash[key] = {} }
10
+
6
11
  module_function
7
12
 
8
13
  def relative_path(path, base_dir = Dir.pwd)
9
- # Optimization for the common case where path begins with the base
10
- # dir. Just cut off the first part.
11
- if path.start_with?(base_dir)
12
- base_dir_length = base_dir.length
13
- result_length = path.length - base_dir_length - 1
14
- return path[base_dir_length + 1, result_length]
15
- end
16
-
17
- path_name = Pathname.new(File.expand_path(path))
18
- begin
19
- path_name.relative_path_from(Pathname.new(base_dir)).to_s
20
- rescue ArgumentError
21
- path
22
- end
14
+ PathUtil.relative_paths_cache[base_dir][path] ||=
15
+ # Optimization for the common case where path begins with the base
16
+ # dir. Just cut off the first part.
17
+ if path.start_with?(base_dir)
18
+ base_dir_length = base_dir.length
19
+ result_length = path.length - base_dir_length - 1
20
+ path[base_dir_length + 1, result_length]
21
+ else
22
+ path_name = Pathname.new(File.expand_path(path))
23
+ begin
24
+ path_name.relative_path_from(Pathname.new(base_dir)).to_s
25
+ rescue ArgumentError
26
+ path
27
+ end
28
+ end
23
29
  end
24
30
 
25
- def smart_path(path)
26
- # Ideally, we calculate this relative to the project root.
27
- base_dir = Dir.pwd
31
+ SMART_PATH_CACHE = {} # rubocop:disable Style/MutableConstant
32
+ private_constant :SMART_PATH_CACHE
28
33
 
29
- if path.start_with? base_dir
30
- relative_path(path, base_dir)
31
- else
32
- path
34
+ def smart_path(path)
35
+ SMART_PATH_CACHE[path] ||= begin
36
+ # Ideally, we calculate this relative to the project root.
37
+ base_dir = Dir.pwd
38
+
39
+ if path.start_with? base_dir
40
+ relative_path(path, base_dir)
41
+ else
42
+ path
43
+ end
33
44
  end
34
45
  end
35
46
 
@@ -40,7 +51,7 @@ module RuboCop
40
51
  matches =
41
52
  if pattern == path
42
53
  true
43
- elsif pattern.match?(/[*{\[?]/)
54
+ elsif glob?(pattern)
44
55
  File.fnmatch?(pattern, path, File::FNM_PATHNAME | File::FNM_EXTGLOB)
45
56
  end
46
57
 
@@ -62,6 +73,11 @@ module RuboCop
62
73
  %r{\A([A-Z]:)?/}i.match?(path)
63
74
  end
64
75
 
76
+ # Returns true for a glob
77
+ def glob?(path)
78
+ path.match?(/[*{\[?]/)
79
+ end
80
+
65
81
  def hidden_file_in_not_hidden_dir?(pattern, path)
66
82
  hidden_file?(path) &&
67
83
  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)
@@ -27,6 +27,11 @@ module RuboCop
27
27
  # @api private
28
28
  MAX_ITERATIONS = 200
29
29
 
30
+ # @api private
31
+ REDUNDANT_COP_DISABLE_DIRECTIVE_RULES = %w[
32
+ Lint/RedundantCopDisableDirective RedundantCopDisableDirective Lint
33
+ ].freeze
34
+
30
35
  attr_reader :errors, :warnings
31
36
  attr_writer :aborting
32
37
 
@@ -194,7 +199,9 @@ module RuboCop
194
199
  end
195
200
 
196
201
  def check_for_redundant_disables?(source)
197
- !source.disabled_line_ranges.empty? && !filtered_run?
202
+ return false if source.disabled_line_ranges.empty? || except_redundant_cop_disable_directive?
203
+
204
+ !@options[:only]
198
205
  end
199
206
 
200
207
  def redundant_cop_disable_directive(file)
@@ -205,8 +212,8 @@ module RuboCop
205
212
  yield cop if cop.relevant_file?(file)
206
213
  end
207
214
 
208
- def filtered_run?
209
- @options[:except] || @options[:only]
215
+ def except_redundant_cop_disable_directive?
216
+ @options[:except] && (@options[:except] & REDUNDANT_COP_DISABLE_DIRECTIVE_RULES).any?
210
217
  end
211
218
 
212
219
  def file_started(file)
@@ -88,7 +88,7 @@ module RuboCop
88
88
  patterns.map! { |dir| File.join(dir, '*') }
89
89
  # We need this special case to avoid creating the pattern
90
90
  # /**/* which searches the entire file system.
91
- patterns = [File.join(dir, '**/*')] if patterns.empty?
91
+ patterns = [File.join(base_dir, '**/*')] if patterns.empty?
92
92
 
93
93
  Dir.glob(patterns, flags).select { |path| FileTest.file?(path) }
94
94
  end
@@ -244,7 +244,6 @@ module RuboCop
244
244
  ].freeze
245
245
 
246
246
  private_constant :SOURCES
247
-
248
247
  def initialize(config)
249
248
  @config = config
250
249
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '1.40.0'
6
+ STRING = '1.43.0'
7
7
 
8
8
  MSG = '%<version>s (using Parser %<parser_version>s, ' \
9
9
  'rubocop-ast %<rubocop_ast_version>s, ' \
data/lib/rubocop.rb CHANGED
@@ -23,8 +23,8 @@ require_relative 'rubocop/ext/processed_source'
23
23
 
24
24
  require_relative 'rubocop/error'
25
25
  require_relative 'rubocop/file_finder'
26
+ require_relative 'rubocop/file_patterns'
26
27
  require_relative 'rubocop/name_similarity'
27
- require_relative 'rubocop/optimized_patterns'
28
28
  require_relative 'rubocop/path_util'
29
29
  require_relative 'rubocop/platform'
30
30
  require_relative 'rubocop/string_interpreter'
@@ -399,6 +399,7 @@ require_relative 'rubocop/cop/lint/useless_access_modifier'
399
399
  require_relative 'rubocop/cop/lint/useless_assignment'
400
400
  require_relative 'rubocop/cop/lint/useless_else_without_rescue'
401
401
  require_relative 'rubocop/cop/lint/useless_method_definition'
402
+ require_relative 'rubocop/cop/lint/useless_rescue'
402
403
  require_relative 'rubocop/cop/lint/useless_ruby2_keywords'
403
404
  require_relative 'rubocop/cop/lint/useless_setter_call'
404
405
  require_relative 'rubocop/cop/lint/useless_times'
@@ -470,6 +471,7 @@ require_relative 'rubocop/cop/style/combinable_loops'
470
471
  require_relative 'rubocop/cop/style/command_literal'
471
472
  require_relative 'rubocop/cop/style/comment_annotation'
472
473
  require_relative 'rubocop/cop/style/commented_keyword'
474
+ require_relative 'rubocop/cop/style/concat_array_literals'
473
475
  require_relative 'rubocop/cop/style/conditional_assignment'
474
476
  require_relative 'rubocop/cop/style/constant_visibility'
475
477
  require_relative 'rubocop/cop/style/copyright'
@@ -537,14 +539,17 @@ require_relative 'rubocop/cop/style/lambda_call'
537
539
  require_relative 'rubocop/cop/style/line_end_concatenation'
538
540
  require_relative 'rubocop/cop/style/magic_comment_format'
539
541
  require_relative 'rubocop/cop/style/map_to_hash'
542
+ require_relative 'rubocop/cop/style/map_to_set'
540
543
  require_relative 'rubocop/cop/style/method_call_without_args_parentheses'
541
544
  require_relative 'rubocop/cop/style/method_call_with_args_parentheses'
545
+ require_relative 'rubocop/cop/style/min_max_comparison'
542
546
  require_relative 'rubocop/cop/style/multiline_in_pattern_then'
543
547
  require_relative 'rubocop/cop/style/numbered_parameters'
544
548
  require_relative 'rubocop/cop/style/open_struct_use'
545
549
  require_relative 'rubocop/cop/style/operator_method_call'
546
550
  require_relative 'rubocop/cop/style/redundant_assignment'
547
551
  require_relative 'rubocop/cop/style/redundant_constant_base'
552
+ require_relative 'rubocop/cop/style/redundant_double_splat_hash_braces'
548
553
  require_relative 'rubocop/cop/style/redundant_each'
549
554
  require_relative 'rubocop/cop/style/redundant_fetch_block'
550
555
  require_relative 'rubocop/cop/style/redundant_file_extension_in_require'
@@ -673,6 +678,7 @@ require_relative 'rubocop/cop/style/while_until_do'
673
678
  require_relative 'rubocop/cop/style/while_until_modifier'
674
679
  require_relative 'rubocop/cop/style/word_array'
675
680
  require_relative 'rubocop/cop/style/yoda_condition'
681
+ require_relative 'rubocop/cop/style/yoda_expression'
676
682
  require_relative 'rubocop/cop/style/zero_length_predicate'
677
683
 
678
684
  require_relative 'rubocop/cop/security/compound_hash'