rubocop 1.66.1 → 1.68.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 (123) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +55 -6
  4. data/config/internal_affairs.yml +11 -0
  5. data/lib/rubocop/cached_data.rb +12 -4
  6. data/lib/rubocop/cli/command/auto_generate_config.rb +6 -7
  7. data/lib/rubocop/cli/command/execute_runner.rb +1 -1
  8. data/lib/rubocop/cli/command/lsp.rb +2 -2
  9. data/lib/rubocop/cli/command/version.rb +2 -2
  10. data/lib/rubocop/config_loader_resolver.rb +3 -3
  11. data/lib/rubocop/config_validator.rb +2 -1
  12. data/lib/rubocop/cop/autocorrect_logic.rb +22 -2
  13. data/lib/rubocop/cop/base.rb +6 -2
  14. data/lib/rubocop/cop/bundler/gem_version.rb +1 -0
  15. data/lib/rubocop/cop/cop.rb +8 -0
  16. data/lib/rubocop/cop/correctors/alignment_corrector.rb +1 -12
  17. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +1 -1
  18. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +10 -0
  19. data/lib/rubocop/cop/internal_affairs/cop_description.rb +0 -4
  20. data/lib/rubocop/cop/internal_affairs/redundant_message_argument.rb +6 -21
  21. data/lib/rubocop/cop/internal_affairs/redundant_source_range.rb +8 -1
  22. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +0 -5
  23. data/lib/rubocop/cop/internal_affairs.rb +16 -0
  24. data/lib/rubocop/cop/layout/access_modifier_indentation.rb +5 -1
  25. data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
  26. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
  27. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +8 -0
  28. data/lib/rubocop/cop/layout/indentation_width.rb +4 -5
  29. data/lib/rubocop/cop/layout/leading_comment_space.rb +56 -1
  30. data/lib/rubocop/cop/layout/space_before_brackets.rb +5 -5
  31. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +4 -0
  32. data/lib/rubocop/cop/lint/ambiguous_range.rb +4 -1
  33. data/lib/rubocop/cop/lint/big_decimal_new.rb +4 -7
  34. data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -1
  35. data/lib/rubocop/cop/lint/duplicate_branch.rb +39 -4
  36. data/lib/rubocop/cop/lint/duplicate_set_element.rb +74 -0
  37. data/lib/rubocop/cop/lint/ensure_return.rb +0 -3
  38. data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
  39. data/lib/rubocop/cop/lint/float_comparison.rb +1 -1
  40. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +10 -4
  41. data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +5 -14
  42. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +25 -2
  43. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +7 -0
  44. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +5 -6
  45. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +1 -1
  46. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +9 -0
  47. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +107 -41
  48. data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
  49. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +88 -0
  50. data/lib/rubocop/cop/lint/uri_regexp.rb +25 -7
  51. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +4 -1
  52. data/lib/rubocop/cop/mixin/check_line_breakable.rb +10 -0
  53. data/lib/rubocop/cop/mixin/endless_method_rewriter.rb +24 -0
  54. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +3 -1
  55. data/lib/rubocop/cop/mixin/percent_array.rb +1 -1
  56. data/lib/rubocop/cop/mixin/statement_modifier.rb +3 -2
  57. data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
  58. data/lib/rubocop/cop/naming/inclusive_language.rb +12 -3
  59. data/lib/rubocop/cop/naming/predicate_name.rb +1 -1
  60. data/lib/rubocop/cop/offense.rb +4 -5
  61. data/lib/rubocop/cop/style/access_modifier_declarations.rb +12 -2
  62. data/lib/rubocop/cop/style/accessor_grouping.rb +10 -2
  63. data/lib/rubocop/cop/style/ambiguous_endless_method_definition.rb +79 -0
  64. data/lib/rubocop/cop/style/arguments_forwarding.rb +46 -6
  65. data/lib/rubocop/cop/style/bitwise_predicate.rb +100 -0
  66. data/lib/rubocop/cop/style/block_delimiters.rb +31 -3
  67. data/lib/rubocop/cop/style/collection_compact.rb +10 -10
  68. data/lib/rubocop/cop/style/combinable_defined.rb +115 -0
  69. data/lib/rubocop/cop/style/combinable_loops.rb +7 -0
  70. data/lib/rubocop/cop/style/commented_keyword.rb +7 -1
  71. data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
  72. data/lib/rubocop/cop/style/data_inheritance.rb +1 -1
  73. data/lib/rubocop/cop/style/endless_method.rb +1 -14
  74. data/lib/rubocop/cop/style/eval_with_location.rb +1 -1
  75. data/lib/rubocop/cop/style/guard_clause.rb +15 -2
  76. data/lib/rubocop/cop/style/hash_each_methods.rb +6 -0
  77. data/lib/rubocop/cop/style/hash_syntax.rb +2 -2
  78. data/lib/rubocop/cop/style/if_inside_else.rb +1 -1
  79. data/lib/rubocop/cop/style/if_with_semicolon.rb +7 -3
  80. data/lib/rubocop/cop/style/keyword_arguments_merging.rb +67 -0
  81. data/lib/rubocop/cop/style/lambda.rb +1 -1
  82. data/lib/rubocop/cop/style/map_into_array.rb +59 -8
  83. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +12 -7
  84. data/lib/rubocop/cop/style/multiline_memoization.rb +1 -1
  85. data/lib/rubocop/cop/style/multiple_comparison.rb +28 -39
  86. data/lib/rubocop/cop/style/nested_modifier.rb +1 -1
  87. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +1 -1
  88. data/lib/rubocop/cop/style/one_line_conditional.rb +4 -0
  89. data/lib/rubocop/cop/style/operator_method_call.rb +25 -6
  90. data/lib/rubocop/cop/style/redundant_begin.rb +4 -0
  91. data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
  92. data/lib/rubocop/cop/style/redundant_line_continuation.rb +23 -5
  93. data/lib/rubocop/cop/style/redundant_parentheses.rb +9 -11
  94. data/lib/rubocop/cop/style/require_order.rb +1 -1
  95. data/lib/rubocop/cop/style/rescue_modifier.rb +13 -1
  96. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +54 -12
  97. data/lib/rubocop/cop/style/safe_navigation.rb +104 -50
  98. data/lib/rubocop/cop/style/safe_navigation_chain_length.rb +52 -0
  99. data/lib/rubocop/cop/style/select_by_regexp.rb +9 -6
  100. data/lib/rubocop/cop/style/semicolon.rb +1 -1
  101. data/lib/rubocop/cop/style/struct_inheritance.rb +1 -1
  102. data/lib/rubocop/cop/style/ternary_parentheses.rb +26 -5
  103. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  104. data/lib/rubocop/cop/team.rb +8 -1
  105. data/lib/rubocop/cop/util.rb +1 -1
  106. data/lib/rubocop/cop/variable_force/assignment.rb +18 -3
  107. data/lib/rubocop/cop/variable_force/branch.rb +1 -1
  108. data/lib/rubocop/cop/variable_force/variable.rb +5 -1
  109. data/lib/rubocop/cop/variable_force/variable_table.rb +2 -2
  110. data/lib/rubocop/cops_documentation_generator.rb +81 -40
  111. data/lib/rubocop/file_finder.rb +9 -4
  112. data/lib/rubocop/formatter/disabled_config_formatter.rb +1 -1
  113. data/lib/rubocop/lsp/runtime.rb +2 -0
  114. data/lib/rubocop/lsp/server.rb +0 -1
  115. data/lib/rubocop/rspec/expect_offense.rb +1 -0
  116. data/lib/rubocop/runner.rb +17 -6
  117. data/lib/rubocop/server/cache.rb +6 -1
  118. data/lib/rubocop/server/core.rb +1 -0
  119. data/lib/rubocop/target_ruby.rb +13 -13
  120. data/lib/rubocop/version.rb +28 -7
  121. data/lib/rubocop/yaml_duplication_checker.rb +20 -27
  122. data/lib/rubocop.rb +10 -0
  123. metadata +13 -4
@@ -179,7 +179,7 @@ module RuboCop
179
179
  end
180
180
 
181
181
  def looks_like_trivial_reader?(node)
182
- !node.arguments? && node.body && node.body.ivar_type?
182
+ !node.arguments? && node.body&.ivar_type?
183
183
  end
184
184
 
185
185
  def trivial_writer?(node)
@@ -9,11 +9,17 @@ module RuboCop
9
9
  # For performance reasons, Team will first dispatch cops & forces in two groups,
10
10
  # first the ones needed for autocorrection (if any), then the rest
11
11
  # (unless autocorrections happened).
12
+ # rubocop:disable Metrics/ClassLength
12
13
  class Team
13
14
  # @return [Team]
14
15
  def self.new(cop_or_classes, config, options = {})
15
16
  # Support v0 api:
16
- return mobilize(cop_or_classes, config, options) if cop_or_classes.first.is_a?(Class)
17
+ if cop_or_classes.first.is_a?(Class)
18
+ warn Rainbow(<<~WARNING).yellow, uplevel: 1
19
+ `Team.new` with cop classes is deprecated. Use `Team.mobilize` instead.
20
+ WARNING
21
+ return mobilize(cop_or_classes, config, options)
22
+ end
17
23
 
18
24
  super
19
25
  end
@@ -279,5 +285,6 @@ module RuboCop
279
285
  end
280
286
  end
281
287
  end
288
+ # rubocop:enable Metrics/ClassLength
282
289
  end
283
290
  end
@@ -32,7 +32,7 @@ module RuboCop
32
32
  end
33
33
 
34
34
  def parentheses?(node)
35
- node.loc.respond_to?(:end) && node.loc.end && node.loc.end.is?(')')
35
+ node.loc.respond_to?(:end) && node.loc.end&.is?(')')
36
36
  end
37
37
 
38
38
  # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
@@ -9,9 +9,10 @@ module RuboCop
9
9
 
10
10
  MULTIPLE_LEFT_HAND_SIDE_TYPE = :mlhs
11
11
 
12
- attr_reader :node, :variable, :referenced, :references
12
+ attr_reader :node, :variable, :referenced, :references, :reassigned
13
13
 
14
14
  alias referenced? referenced
15
+ alias reassigned? reassigned
15
16
 
16
17
  def initialize(node, variable)
17
18
  unless VARIABLE_ASSIGNMENT_TYPES.include?(node.type)
@@ -24,6 +25,7 @@ module RuboCop
24
25
  @variable = variable
25
26
  @referenced = false
26
27
  @references = []
28
+ @reassigned = false
27
29
  end
28
30
 
29
31
  def name
@@ -39,8 +41,14 @@ module RuboCop
39
41
  @referenced = true
40
42
  end
41
43
 
44
+ def reassigned!
45
+ return if referenced?
46
+
47
+ @reassigned = true
48
+ end
49
+
42
50
  def used?
43
- @variable.captured_by_block? || @referenced
51
+ (!reassigned? && @variable.captured_by_block?) || @referenced
44
52
  end
45
53
 
46
54
  def regexp_named_capture?
@@ -102,6 +110,7 @@ module RuboCop
102
110
  end
103
111
 
104
112
  def multiple_assignment_node
113
+ return nil unless node.parent&.mlhs_type?
105
114
  return nil unless (grandparent_node = node.parent&.parent)
106
115
  if (node = find_multiple_assignment_node(grandparent_node))
107
116
  return node
@@ -119,7 +128,13 @@ module RuboCop
119
128
  end
120
129
 
121
130
  def for_assignment_node
122
- node.ancestors.find(&:for_type?)
131
+ return unless (parent_node = node.parent)
132
+ return parent_node if parent_node.for_type?
133
+
134
+ grandparent_node = parent_node.parent
135
+ return grandparent_node if parent_node.mlhs_type? && grandparent_node&.for_type?
136
+
137
+ nil
123
138
  end
124
139
 
125
140
  def find_multiple_assignment_node(grandparent_node)
@@ -74,7 +74,7 @@ module RuboCop
74
74
  def parent
75
75
  return @parent if instance_variable_defined?(:@parent)
76
76
 
77
- @branch = Branch.of(control_node, scope: scope)
77
+ @parent = Branch.of(control_node, scope: scope)
78
78
  end
79
79
 
80
80
  def each_ancestor(include_self: false, &block)
@@ -29,7 +29,11 @@ module RuboCop
29
29
  end
30
30
 
31
31
  def assign(node)
32
- @assignments << Assignment.new(node, self)
32
+ assignment = Assignment.new(node, self)
33
+
34
+ @assignments.last&.reassigned! unless captured_by_block?
35
+
36
+ @assignments << assignment
33
37
  end
34
38
 
35
39
  def referenced?
@@ -61,8 +61,8 @@ module RuboCop
61
61
  "at #{node.source_range}, #{node.inspect}"
62
62
  end
63
63
 
64
- variable.assign(node)
65
64
  mark_variable_as_captured_by_block_if_so(variable)
65
+ variable.assign(node)
66
66
  end
67
67
 
68
68
  def reference_variable(name, node)
@@ -87,8 +87,8 @@ module RuboCop
87
87
  # So just skip.
88
88
  return unless variable
89
89
 
90
- variable.reference!(node)
91
90
  mark_variable_as_captured_by_block_if_so(variable)
91
+ variable.reference!(node)
92
92
  end
93
93
 
94
94
  def find_variable(name)
@@ -1,23 +1,49 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'fileutils'
4
+ require 'yard'
4
5
 
5
6
  # Class for generating documentation of all cops departments
6
7
  # @api private
7
8
  class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
8
9
  include ::RuboCop::Cop::Documentation
10
+ CopData = Struct.new(
11
+ :cop, :description, :example_objects, :safety_objects, :see_objects, :config, keyword_init: true
12
+ )
13
+
14
+ STRUCTURE = {
15
+ name: ->(data) { cop_header(data.cop) },
16
+ required_ruby_version: ->(data) { required_ruby_version(data.cop) },
17
+ properties: ->(data) { properties(data.cop) },
18
+ description: ->(data) { "#{data.description}\n" },
19
+ safety: ->(data) { safety_object(data.safety_objects, data.cop) },
20
+ examples: ->(data) { examples(data.example_objects, data.cop) },
21
+ configuration: ->(data) { configurations(data.cop.department, data.config, data.cop) },
22
+ references: ->(data) { references(data.cop, data.see_objects) }
23
+ }.freeze
24
+
9
25
  # This class will only generate documentation for cops that belong to one of
10
26
  # the departments given in the `departments` array. E.g. if we only wanted
11
27
  # documentation for Lint cops:
12
28
  #
13
29
  # CopsDocumentationGenerator.new(departments: ['Lint']).call
14
30
  #
15
- def initialize(departments: [])
31
+ # You can append additional information:
32
+ #
33
+ # callback = ->(data) { required_rails_version(data.cop) }
34
+ # CopsDocumentationGenerator.new(extra_info: { ruby_version: callback }).call
35
+ #
36
+ # This will insert the string returned from the lambda _after_ the section from RuboCop itself.
37
+ # See `CopsDocumentationGenerator::STRUCTURE` for available sections.
38
+ #
39
+ def initialize(departments: [], extra_info: {}, base_dir: Dir.pwd)
16
40
  @departments = departments.map(&:to_sym).sort!
41
+ @extra_info = extra_info
17
42
  @cops = RuboCop::Cop::Registry.global
18
43
  @config = RuboCop::ConfigLoader.default_configuration
19
- @docs_path = "#{Dir.pwd}/docs/modules/ROOT/pages/"
20
- FileUtils.mkdir_p(@docs_path)
44
+ @base_dir = base_dir
45
+ @docs_path = "#{base_dir}/docs/modules/ROOT"
46
+ FileUtils.mkdir_p("#{@docs_path}/pages")
21
47
  end
22
48
 
23
49
  def call
@@ -37,24 +63,21 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
37
63
  cops.with_department(department).sort!
38
64
  end
39
65
 
40
- def cops_body(cop, description, examples_objects, safety_objects, see_objects, pars) # rubocop:disable Metrics/AbcSize, Metrics/ParameterLists
41
- check_examples_to_have_the_default_enforced_style!(examples_objects, cop)
42
-
43
- content = h2(cop.cop_name)
44
- content << required_ruby_version(cop)
45
- content << properties(cop)
46
- content << "#{description}\n"
47
- content << safety_object(safety_objects) if safety_objects.any? { |s| !s.text.blank? }
48
- content << examples(examples_objects) if examples_objects.any?
49
- content << configurations(cop.department, pars)
50
- content << references(cop, see_objects)
66
+ def cops_body(data)
67
+ check_examples_to_have_the_default_enforced_style!(data.example_objects, data.cop)
68
+
69
+ content = +''
70
+ STRUCTURE.each do |section, block|
71
+ content << instance_exec(data, &block)
72
+ content << @extra_info[section].call(data) if @extra_info[section]
73
+ end
51
74
  content
52
75
  end
53
76
 
54
- def check_examples_to_have_the_default_enforced_style!(examples_object, cop)
55
- return if examples_object.none?
77
+ def check_examples_to_have_the_default_enforced_style!(example_objects, cop)
78
+ return if example_objects.none?
56
79
 
57
- examples_describing_enforced_style = examples_object.map(&:name).grep(/EnforcedStyle:/)
80
+ examples_describing_enforced_style = example_objects.map(&:name).grep(/EnforcedStyle:/)
58
81
  return if examples_describing_enforced_style.none?
59
82
 
60
83
  if examples_describing_enforced_style.index { |name| name.match?('default') }.nonzero?
@@ -66,16 +89,20 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
66
89
  raise "Specify the default EnforcedStyle for #{cop.cop_name}"
67
90
  end
68
91
 
69
- def examples(examples_object)
70
- examples_object.each_with_object(h3('Examples').dup) do |example, content|
92
+ def examples(example_objects, cop)
93
+ return '' if example_objects.none?
94
+
95
+ example_objects.each_with_object(cop_subsection('Examples', cop).dup) do |example, content|
71
96
  content << "\n" unless content.end_with?("\n\n")
72
- content << h4(example.name) unless example.name == ''
97
+ content << example_header(example.name, cop) unless example.name == ''
73
98
  content << code_example(example)
74
99
  end
75
100
  end
76
101
 
77
- def safety_object(safety_object_objects)
78
- safety_object_objects.each_with_object(h3('Safety').dup) do |safety_object, content|
102
+ def safety_object(safety_objects, cop)
103
+ return '' if safety_objects.all? { |s| s.text.blank? }
104
+
105
+ safety_objects.each_with_object(cop_subsection('Safety', cop).dup) do |safety_object, content|
79
106
  next if safety_object.text.blank?
80
107
 
81
108
  content << "\n" unless content.end_with?("\n\n")
@@ -115,22 +142,25 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
115
142
  end
116
143
  # rubocop:enable Metrics/MethodLength
117
144
 
118
- def h2(title)
145
+ def cop_header(cop)
119
146
  content = +"\n"
120
- content << "== #{title}\n"
147
+ content << "[##{to_anchor(cop.cop_name)}]\n"
148
+ content << "== #{cop.cop_name}\n"
121
149
  content << "\n"
122
150
  content
123
151
  end
124
152
 
125
- def h3(title)
153
+ def cop_subsection(title, cop)
126
154
  content = +"\n"
155
+ content << "[##{to_anchor(title)}-#{to_anchor(cop.cop_name)}]\n"
127
156
  content << "=== #{title}\n"
128
157
  content << "\n"
129
158
  content
130
159
  end
131
160
 
132
- def h4(title)
133
- content = +"==== #{title}\n"
161
+ def example_header(title, cop)
162
+ content = +"[##{to_anchor(title)}-#{to_anchor(cop.cop_name)}]\n"
163
+ content << "==== #{title}\n"
134
164
  content << "\n"
135
165
  content
136
166
  end
@@ -142,7 +172,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
142
172
  content
143
173
  end
144
174
 
145
- def configurations(department, pars)
175
+ def configurations(department, pars, cop)
146
176
  return '' if pars.empty?
147
177
 
148
178
  header = ['Name', 'Default value', 'Configurable values']
@@ -157,7 +187,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
157
187
  [configuration_name(department, name), default, configurable]
158
188
  end
159
189
 
160
- h3('Configurable attributes') + to_table(header, content)
190
+ cop_subsection('Configurable attributes', cop) + to_table(header, content)
161
191
  end
162
192
 
163
193
  def configuration_name(department, name)
@@ -218,7 +248,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
218
248
  else
219
249
  wrap_backtick(val.nil? ? '<none>' : val)
220
250
  end
221
- value.gsub("#{Dir.pwd}/", '').rstrip
251
+ value.gsub("#{@base_dir}/", '').rstrip
222
252
  end
223
253
 
224
254
  def wrap_backtick(value)
@@ -235,7 +265,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
235
265
  urls = RuboCop::Cop::MessageAnnotator.new(config, cop.name, cop_config, {}).urls
236
266
  return '' if urls.empty? && see_objects.empty?
237
267
 
238
- content = h3('References')
268
+ content = cop_subsection('References', cop)
239
269
  content << urls.map { |url| "* #{url}" }.join("\n")
240
270
  content << "\n" unless urls.empty?
241
271
  content << see_objects.map { |see| "* #{see.name}" }.join("\n")
@@ -247,7 +277,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
247
277
  return '' unless department == :Layout
248
278
 
249
279
  filename = "#{department_to_basename(department)}_footer.adoc"
250
- file = "#{Dir.pwd}/docs/modules/ROOT/partials/#{filename}"
280
+ file = "#{docs_path}/partials/#{filename}"
251
281
  return '' unless File.exist?(file)
252
282
 
253
283
  "\ninclude::../partials/#{filename}[]\n"
@@ -267,7 +297,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
267
297
  HEADER
268
298
  selected_cops.each { |cop| content << print_cop_with_doc(cop) }
269
299
  content << footer_for_department(department)
270
- file_name = "#{docs_path}/#{department_to_basename(department)}.adoc"
300
+ file_name = "#{docs_path}/pages/#{department_to_basename(department)}.adoc"
271
301
  File.open(file_name, 'w') do |file|
272
302
  puts "* generated #{file_name}"
273
303
  file.write("#{content.strip}\n")
@@ -283,14 +313,16 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
283
313
  ]
284
314
  pars = cop_config.reject { |k| non_display_keys.include? k }
285
315
  description = 'No documentation'
286
- examples_object = safety_object = see_object = []
316
+ example_objects = safety_objects = see_objects = []
287
317
  cop_code(cop) do |code_object|
288
318
  description = code_object.docstring unless code_object.docstring.blank?
289
- examples_object = code_object.tags('example')
290
- safety_object = code_object.tags('safety')
291
- see_object = code_object.tags('see')
319
+ example_objects = code_object.tags('example')
320
+ safety_objects = code_object.tags('safety')
321
+ see_objects = code_object.tags('see')
292
322
  end
293
- cops_body(cop, description, examples_object, safety_object, see_object, pars)
323
+ data = CopData.new(cop: cop, description: description, example_objects: example_objects,
324
+ safety_objects: safety_objects, see_objects: see_objects, config: pars)
325
+ cops_body(data)
294
326
  end
295
327
 
296
328
  def cop_code(cop)
@@ -306,7 +338,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
306
338
  filename = "#{department_to_basename(department)}.adoc"
307
339
  content = +"=== Department xref:#{filename}[#{type_title}]\n\n"
308
340
  cops_of_department(department).each do |cop|
309
- anchor = cop.cop_name.delete('/').downcase
341
+ anchor = to_anchor(cop.cop_name)
310
342
  content << "* xref:#{filename}##{anchor}[#{cop.cop_name}]\n"
311
343
  end
312
344
 
@@ -314,7 +346,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
314
346
  end
315
347
 
316
348
  def print_table_of_contents
317
- path = "#{docs_path}/cops.adoc"
349
+ path = "#{docs_path}/pages/cops.adoc"
318
350
 
319
351
  File.write(path, table_contents) and return unless File.exist?(path)
320
352
 
@@ -338,4 +370,13 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
338
370
 
339
371
  status == 'pending' ? 'Pending' : 'Enabled'
340
372
  end
373
+
374
+ # HTML anchor are somewhat limited in what characters they can contain, just
375
+ # accept a known-good subset. As long as it's consistent it doesn't matter.
376
+ #
377
+ # Style/AccessModifierDeclarations => styleaccessmodifierdeclarations
378
+ # OnlyFor: [] (default) => onlyfor_-__-_default_
379
+ def to_anchor(title)
380
+ title.delete('/').tr(' ', '-').gsub(/[^a-zA-Z0-9-]/, '_').downcase
381
+ end
341
382
  end
@@ -23,15 +23,20 @@ module RuboCop
23
23
  last_file
24
24
  end
25
25
 
26
+ def traverse_directories_upwards(start_dir, stop_dir = nil)
27
+ Pathname.new(start_dir).expand_path.ascend do |dir|
28
+ yield(dir)
29
+ dir = dir.to_s
30
+ break if dir == stop_dir || dir == FileFinder.root_level
31
+ end
32
+ end
33
+
26
34
  private
27
35
 
28
36
  def traverse_files_upwards(filename, start_dir, stop_dir)
29
- Pathname.new(start_dir).expand_path.ascend do |dir|
37
+ traverse_directories_upwards(start_dir, stop_dir) do |dir|
30
38
  file = dir + filename
31
39
  yield(file.to_s) if file.exist?
32
-
33
- dir = dir.to_s
34
- break if dir == stop_dir || dir == FileFinder.root_level
35
40
  end
36
41
  end
37
42
  end
@@ -10,7 +10,7 @@ module RuboCop
10
10
  HEADING = <<~COMMENTS
11
11
  # This configuration was generated by
12
12
  # `%<command>s`
13
- # %<timestamp>susing RuboCop version #{Version.version}.
13
+ # %<timestamp>susing RuboCop version #{Version::STRING}.
14
14
  # The point is for the user to remove these configuration records
15
15
  # one by one as the offenses are removed from the code base.
16
16
  # Note that changes in the inspected code, or installation of new
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'stringio'
4
+
3
5
  #
4
6
  # This code is based on https://github.com/standardrb/standard.
5
7
  #
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'language_server-protocol'
4
- require_relative '../lsp'
5
4
  require_relative 'logger'
6
5
  require_relative 'routes'
7
6
  require_relative 'runtime'
@@ -169,6 +169,7 @@ module RuboCop
169
169
  raise 'Expected correction but no corrections were made' if new_source == source
170
170
 
171
171
  expect(new_source).to eq(correction)
172
+ expect(@processed_source).to be_valid_syntax, 'Expected correction to be valid syntax'
172
173
  end
173
174
  # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
174
175
 
@@ -139,7 +139,7 @@ module RuboCop
139
139
  offenses = process_file(file)
140
140
  yield file
141
141
 
142
- if offenses.any? { |o| considered_failure?(o) }
142
+ if offenses.any? { |o| considered_failure?(o) && offense_displayed?(o) }
143
143
  break false if @options[:fail_fast]
144
144
 
145
145
  next false
@@ -362,6 +362,13 @@ module RuboCop
362
362
  self.class.ruby_extractors.find do |ruby_extractor|
363
363
  result = ruby_extractor.call(processed_source)
364
364
  break result if result
365
+ rescue StandardError
366
+ location = if ruby_extractor.is_a?(Proc)
367
+ ruby_extractor.source_location
368
+ else
369
+ ruby_extractor.method(:call).source_location
370
+ end
371
+ raise Error, "Ruby extractor #{location[0]} failed to process #{processed_source.path}."
365
372
  end
366
373
  end
367
374
 
@@ -433,18 +440,22 @@ module RuboCop
433
440
  !offense.corrected? && offense.severity >= minimum_severity_to_fail
434
441
  end
435
442
 
436
- def offenses_to_report(offenses)
443
+ def offense_displayed?(offense)
437
444
  if @options[:display_only_fail_level_offenses]
438
- offenses.select { |o| considered_failure?(o) }
445
+ considered_failure?(offense)
439
446
  elsif @options[:display_only_safe_correctable]
440
- offenses.select { |o| supports_safe_autocorrect?(o) }
447
+ supports_safe_autocorrect?(offense)
441
448
  elsif @options[:display_only_correctable]
442
- offenses.select(&:correctable?)
449
+ offense.correctable?
443
450
  else
444
- offenses
451
+ true
445
452
  end
446
453
  end
447
454
 
455
+ def offenses_to_report(offenses)
456
+ offenses.select { |o| offense_displayed?(o) }
457
+ end
458
+
448
459
  def supports_safe_autocorrect?(offense)
449
460
  cop_class = Cop::Registry.global.find_by_cop_name(offense.cop_name)
450
461
  default_cfg = default_config(offense.cop_name)
@@ -43,13 +43,18 @@ module RuboCop
43
43
  @project_dir_cache_key ||= project_dir[1..].tr('/', '+')
44
44
  end
45
45
 
46
+ # rubocop:disable Metrics/AbcSize
46
47
  def restart_key
47
48
  lockfile_path = LOCKFILE_NAMES.map do |lockfile_name|
48
49
  Pathname(project_dir).join(lockfile_name)
49
50
  end.find(&:exist?)
51
+ version_data = lockfile_path&.read || RuboCop::Version::STRING
52
+ config_data = Pathname(ConfigFinder.find_config_path(Dir.pwd)).read
53
+ todo_data = (rubocop_todo = Pathname('.rubocop_todo.yml')).exist? ? rubocop_todo.read : ''
50
54
 
51
- Digest::SHA1.hexdigest(lockfile_path&.read || RuboCop::Version::STRING)
55
+ Digest::SHA1.hexdigest(version_data + config_data + todo_data)
52
56
  end
57
+ # rubocop:enable Metrics/AbcSize
53
58
 
54
59
  def dir
55
60
  Pathname.new(File.join(cache_path, project_dir_cache_key)).tap do |d|
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'securerandom'
4
4
  require 'socket'
5
+ require 'stringio'
5
6
 
6
7
  #
7
8
  # This code is based on https://github.com/fohte/rubocop-daemon.
@@ -53,8 +53,6 @@ module RuboCop
53
53
  class GemspecFile < Source
54
54
  extend NodePattern::Macros
55
55
 
56
- GEMSPEC_EXTENSION = '.gemspec'
57
-
58
56
  # @!method required_ruby_version(node)
59
57
  def_node_search :required_ruby_version, <<~PATTERN
60
58
  (send _ :required_ruby_version= $_)
@@ -68,7 +66,7 @@ module RuboCop
68
66
  PATTERN
69
67
 
70
68
  def name
71
- "`required_ruby_version` parameter (in #{gemspec_filename})"
69
+ "`required_ruby_version` parameter (in #{gemspec_filepath})"
72
70
  end
73
71
 
74
72
  private
@@ -83,16 +81,18 @@ module RuboCop
83
81
  find_minimal_known_ruby(right_hand_side)
84
82
  end
85
83
 
86
- def gemspec_filename
87
- @gemspec_filename ||= begin
88
- basename = Pathname.new(@config.base_dir_for_path_parameters).basename.to_s
89
- "#{basename}#{GEMSPEC_EXTENSION}"
90
- end
91
- end
92
-
93
84
  def gemspec_filepath
94
- @gemspec_filepath ||=
95
- @config.find_file_upwards(gemspec_filename, @config.base_dir_for_path_parameters)
85
+ return @gemspec_filepath if defined?(@gemspec_filepath)
86
+
87
+ @gemspec_filepath =
88
+ @config.traverse_directories_upwards(@config.base_dir_for_path_parameters) do |dir|
89
+ # NOTE: Can't use `dir.glob` because of JRuby 9.4.8.0 incompatibility:
90
+ # https://github.com/jruby/jruby/issues/8358
91
+ candidates = Pathname.glob("#{dir}/*.gemspec")
92
+ # Bundler will use a gemspec whatever the filename is, as long as its the only one in
93
+ # the folder.
94
+ break candidates.first if candidates.one?
95
+ end
96
96
  end
97
97
 
98
98
  def version_from_gemspec_file(file)
@@ -273,7 +273,7 @@ module RuboCop
273
273
 
274
274
  def rubocop_version_with_support
275
275
  if supported?
276
- RuboCop::Version.version
276
+ RuboCop::Version::STRING
277
277
  else
278
278
  OBSOLETE_RUBIES[version]
279
279
  end
@@ -3,10 +3,11 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '1.66.1'
6
+ STRING = '1.68.0'
7
7
 
8
8
  MSG = '%<version>s (using %<parser_version>s, ' \
9
9
  'rubocop-ast %<rubocop_ast_version>s, ' \
10
+ 'analyzing as Ruby %<target_ruby_version>s, ' \
10
11
  'running on %<ruby_engine>s %<ruby_version>s)%<server_mode>s [%<ruby_platform>s]'
11
12
 
12
13
  CANONICAL_FEATURE_NAMES = {
@@ -17,11 +18,13 @@ module RuboCop
17
18
  'rubocop-md' => 'markdown', 'rubocop-factory_bot' => 'factory_bot'
18
19
  }.freeze
19
20
 
21
+ # NOTE: Marked as private but used by gems like standard.
20
22
  # @api private
21
23
  def self.version(debug: false, env: nil)
22
24
  if debug
23
25
  verbose_version = format(MSG, version: STRING, parser_version: parser_version,
24
26
  rubocop_ast_version: RuboCop::AST::Version::STRING,
27
+ target_ruby_version: target_ruby_version(env),
25
28
  ruby_engine: RUBY_ENGINE, ruby_version: RUBY_VERSION,
26
29
  server_mode: server_mode,
27
30
  ruby_platform: RUBY_PLATFORM)
@@ -39,6 +42,11 @@ module RuboCop
39
42
  end
40
43
  end
41
44
 
45
+ # @api private
46
+ def self.verbose(env: nil)
47
+ version(debug: true, env: env)
48
+ end
49
+
42
50
  # @api private
43
51
  def self.parser_version
44
52
  config_path = ConfigFinder.find_config_path(Dir.pwd)
@@ -56,12 +64,7 @@ module RuboCop
56
64
 
57
65
  # @api private
58
66
  def self.extension_versions(env)
59
- features = Util.silence_warnings do
60
- # Suppress any config issues when loading the config (ie. deprecations,
61
- # pending cops, etc.).
62
- env.config_store.unvalidated.for_pwd.loaded_features.sort
63
- end
64
-
67
+ features = config_for_pwd(env).loaded_features.sort
65
68
  features.filter_map do |loaded_feature|
66
69
  next unless (match = loaded_feature.match(/rubocop-(?<feature>.*)/))
67
70
 
@@ -83,6 +86,24 @@ module RuboCop
83
86
  end
84
87
  end
85
88
 
89
+ # @api private
90
+ def self.target_ruby_version(env)
91
+ if env
92
+ config_for_pwd(env).target_ruby_version
93
+ else
94
+ TargetRuby.new(Config.new).version
95
+ end
96
+ end
97
+
98
+ # @api private
99
+ def self.config_for_pwd(env)
100
+ Util.silence_warnings do
101
+ # Suppress any config issues when loading the config (ie. deprecations,
102
+ # pending cops, etc.).
103
+ env.config_store.unvalidated.for_pwd
104
+ end
105
+ end
106
+
86
107
  # Returns feature version in one of two ways:
87
108
  #
88
109
  # * Find by RuboCop core version style (e.g. rubocop-performance, rubocop-rspec)