rubocop 1.66.1 → 1.68.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/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)