rubocop 1.53.1 → 1.57.2

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 (132) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -1
  3. data/config/default.yml +34 -8
  4. data/config/obsoletion.yml +5 -0
  5. data/lib/rubocop/cli/command/auto_generate_config.rb +10 -5
  6. data/lib/rubocop/cli.rb +1 -1
  7. data/lib/rubocop/config_finder.rb +2 -2
  8. data/lib/rubocop/config_obsoletion/parameter_rule.rb +9 -1
  9. data/lib/rubocop/cop/autocorrect_logic.rb +3 -1
  10. data/lib/rubocop/cop/base.rb +1 -1
  11. data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -0
  12. data/lib/rubocop/cop/bundler/duplicated_group.rb +127 -0
  13. data/lib/rubocop/cop/bundler/ordered_gems.rb +9 -1
  14. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +7 -4
  15. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +9 -1
  16. data/lib/rubocop/cop/generator/require_file_injector.rb +1 -1
  17. data/lib/rubocop/cop/internal_affairs/example_description.rb +42 -21
  18. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +3 -1
  19. data/lib/rubocop/cop/internal_affairs/redundant_method_dispatch_node.rb +11 -2
  20. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +2 -0
  21. data/lib/rubocop/cop/layout/dot_position.rb +1 -5
  22. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +42 -9
  23. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +26 -3
  24. data/lib/rubocop/cop/layout/end_alignment.rb +7 -1
  25. data/lib/rubocop/cop/layout/heredoc_indentation.rb +3 -0
  26. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  27. data/lib/rubocop/cop/layout/leading_comment_space.rb +1 -1
  28. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +17 -9
  29. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
  30. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +2 -0
  31. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +18 -3
  32. data/lib/rubocop/cop/layout/redundant_line_break.rb +13 -3
  33. data/lib/rubocop/cop/layout/space_after_comma.rb +9 -1
  34. data/lib/rubocop/cop/layout/space_after_not.rb +1 -1
  35. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +2 -2
  36. data/lib/rubocop/cop/layout/space_around_operators.rb +3 -1
  37. data/lib/rubocop/cop/layout/space_inside_parens.rb +1 -1
  38. data/lib/rubocop/cop/layout/trailing_empty_lines.rb +5 -0
  39. data/lib/rubocop/cop/lint/debugger.rb +17 -4
  40. data/lib/rubocop/cop/lint/empty_block.rb +1 -1
  41. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
  42. data/lib/rubocop/cop/lint/mixed_case_range.rb +3 -1
  43. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +10 -7
  44. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +10 -0
  45. data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -0
  46. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +20 -4
  47. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +11 -4
  48. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +7 -1
  49. data/lib/rubocop/cop/lint/struct_new_override.rb +12 -12
  50. data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
  51. data/lib/rubocop/cop/lint/to_enum_arguments.rb +5 -3
  52. data/lib/rubocop/cop/lint/useless_assignment.rb +38 -12
  53. data/lib/rubocop/cop/lint/void.rb +32 -20
  54. data/lib/rubocop/cop/metrics/block_length.rb +1 -1
  55. data/lib/rubocop/cop/metrics/class_length.rb +8 -3
  56. data/lib/rubocop/cop/metrics/method_length.rb +1 -1
  57. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +32 -4
  58. data/lib/rubocop/cop/mixin/comments_help.rb +16 -12
  59. data/lib/rubocop/cop/mixin/def_node.rb +1 -1
  60. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +14 -11
  61. data/lib/rubocop/cop/mixin/heredoc.rb +6 -2
  62. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +3 -2
  63. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +5 -7
  64. data/lib/rubocop/cop/mixin/string_help.rb +4 -2
  65. data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
  66. data/lib/rubocop/cop/naming/file_name.rb +1 -1
  67. data/lib/rubocop/cop/naming/heredoc_delimiter_naming.rb +3 -1
  68. data/lib/rubocop/cop/style/alias.rb +9 -8
  69. data/lib/rubocop/cop/style/arguments_forwarding.rb +280 -63
  70. data/lib/rubocop/cop/style/array_intersect.rb +13 -5
  71. data/lib/rubocop/cop/style/block_delimiters.rb +2 -1
  72. data/lib/rubocop/cop/style/class_equality_comparison.rb +7 -0
  73. data/lib/rubocop/cop/style/collection_methods.rb +2 -0
  74. data/lib/rubocop/cop/style/combinable_loops.rb +4 -2
  75. data/lib/rubocop/cop/style/concat_array_literals.rb +1 -1
  76. data/lib/rubocop/cop/style/empty_case_condition.rb +6 -1
  77. data/lib/rubocop/cop/style/for.rb +1 -1
  78. data/lib/rubocop/cop/style/format_string.rb +24 -3
  79. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +3 -1
  80. data/lib/rubocop/cop/style/guard_clause.rb +26 -0
  81. data/lib/rubocop/cop/style/hash_conversion.rb +10 -0
  82. data/lib/rubocop/cop/style/identical_conditional_branches.rb +25 -3
  83. data/lib/rubocop/cop/style/if_with_semicolon.rb +2 -2
  84. data/lib/rubocop/cop/style/lambda.rb +3 -3
  85. data/lib/rubocop/cop/style/lambda_call.rb +5 -0
  86. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +8 -1
  87. data/lib/rubocop/cop/style/mixin_grouping.rb +1 -1
  88. data/lib/rubocop/cop/style/multiline_block_chain.rb +1 -1
  89. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +1 -1
  90. data/lib/rubocop/cop/style/nested_ternary_operator.rb +3 -11
  91. data/lib/rubocop/cop/style/open_struct_use.rb +1 -1
  92. data/lib/rubocop/cop/style/operator_method_call.rb +6 -0
  93. data/lib/rubocop/cop/style/redundant_argument.rb +6 -1
  94. data/lib/rubocop/cop/style/redundant_begin.rb +9 -1
  95. data/lib/rubocop/cop/style/redundant_conditional.rb +1 -9
  96. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +93 -5
  97. data/lib/rubocop/cop/style/redundant_exception.rb +32 -12
  98. data/lib/rubocop/cop/style/redundant_filter_chain.rb +22 -5
  99. data/lib/rubocop/cop/style/redundant_parentheses.rb +41 -15
  100. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +4 -1
  101. data/lib/rubocop/cop/style/redundant_return.rb +7 -2
  102. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +5 -0
  103. data/lib/rubocop/cop/style/return_nil.rb +6 -2
  104. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +23 -9
  105. data/lib/rubocop/cop/style/semicolon.rb +0 -3
  106. data/lib/rubocop/cop/style/single_argument_dig.rb +2 -1
  107. data/lib/rubocop/cop/style/single_line_do_end_block.rb +67 -0
  108. data/lib/rubocop/cop/style/sole_nested_conditional.rb +3 -1
  109. data/lib/rubocop/cop/style/string_literals_in_interpolation.rb +30 -5
  110. data/lib/rubocop/cop/style/symbol_array.rb +35 -15
  111. data/lib/rubocop/cop/style/yoda_condition.rb +4 -2
  112. data/lib/rubocop/cop/style/yoda_expression.rb +8 -7
  113. data/lib/rubocop/cop/utils/regexp_ranges.rb +26 -13
  114. data/lib/rubocop/cop/variable_force/assignment.rb +14 -5
  115. data/lib/rubocop/file_finder.rb +4 -7
  116. data/lib/rubocop/formatter/html_formatter.rb +4 -2
  117. data/lib/rubocop/formatter/junit_formatter.rb +1 -1
  118. data/lib/rubocop/lsp/routes.rb +41 -18
  119. data/lib/rubocop/lsp/runtime.rb +22 -2
  120. data/lib/rubocop/lsp/server.rb +10 -4
  121. data/lib/rubocop/magic_comment.rb +12 -10
  122. data/lib/rubocop/result_cache.rb +4 -0
  123. data/lib/rubocop/rspec/shared_contexts.rb +2 -3
  124. data/lib/rubocop/runner.rb +5 -3
  125. data/lib/rubocop/server/cache.rb +1 -0
  126. data/lib/rubocop/server/client_command/exec.rb +1 -1
  127. data/lib/rubocop/string_interpreter.rb +3 -3
  128. data/lib/rubocop/target_finder.rb +7 -3
  129. data/lib/rubocop/target_ruby.rb +9 -5
  130. data/lib/rubocop/version.rb +1 -1
  131. data/lib/rubocop.rb +2 -0
  132. metadata +16 -14
@@ -6,27 +6,40 @@ module RuboCop
6
6
  # Helper to abstract complexity of building range pairs
7
7
  # with octal escape reconstruction (needed for regexp_parser < 2.7).
8
8
  class RegexpRanges
9
- attr_reader :compound_token, :root
9
+ attr_reader :root
10
10
 
11
11
  def initialize(root)
12
12
  @root = root
13
13
  @compound_token = []
14
+ @pairs = []
15
+ @populated = false
16
+ end
17
+
18
+ def compound_token
19
+ populate_all unless @populated
20
+
21
+ @compound_token
14
22
  end
15
23
 
16
24
  def pairs
17
- unless @pairs
18
- @pairs = []
19
- populate(root)
20
- end
25
+ populate_all unless @populated
26
+
27
+ @pairs
28
+ end
29
+
30
+ private
31
+
32
+ def populate_all
33
+ populate(@root)
21
34
 
22
35
  # If either bound is a compound the first one is an escape
23
36
  # and that's all we need to work with.
24
37
  # If there are any cops that wanted to operate on the compound
25
38
  # expression we could wrap it with a facade class.
26
- @pairs.map { |pair| pair.map(&:first) }
27
- end
39
+ @pairs.map! { |pair| pair.map(&:first) }
28
40
 
29
- private
41
+ @populated = true
42
+ end
30
43
 
31
44
  def populate(expr)
32
45
  expressions = expr.expressions.to_a
@@ -35,15 +48,15 @@ module RuboCop
35
48
  current = expressions.shift
36
49
 
37
50
  if escaped_octal?(current)
38
- compound_token << current
39
- compound_token.concat(pop_octal_digits(expressions))
51
+ @compound_token << current
52
+ @compound_token.concat(pop_octal_digits(expressions))
40
53
  # If we have all the digits we can discard.
41
54
  end
42
55
 
43
56
  next unless current.type == :set
44
57
 
45
58
  process_set(expressions, current)
46
- compound_token.clear
59
+ @compound_token.clear
47
60
  end
48
61
  end
49
62
 
@@ -64,8 +77,8 @@ module RuboCop
64
77
 
65
78
  def compose_range(expressions, current)
66
79
  range_start, range_end = current.expressions
67
- range_start = if compound_token.size.between?(1, 2) && octal_digit?(range_start.text)
68
- compound_token.dup << range_start
80
+ range_start = if @compound_token.size.between?(1, 2) && octal_digit?(range_start.text)
81
+ @compound_token.dup << range_start
69
82
  else
70
83
  [range_start]
71
84
  end
@@ -102,10 +102,11 @@ module RuboCop
102
102
  end
103
103
 
104
104
  def multiple_assignment_node
105
- grandparent_node = node.parent&.parent
106
- return nil unless grandparent_node
105
+ return nil unless (grandparent_node = node.parent&.parent)
106
+ if (node = find_multiple_assignment_node(grandparent_node))
107
+ return node
108
+ end
107
109
  return nil unless grandparent_node.type == MULTIPLE_ASSIGNMENT_TYPE
108
- return nil unless node.parent.type == MULTIPLE_LEFT_HAND_SIDE_TYPE
109
110
 
110
111
  grandparent_node
111
112
  end
@@ -118,9 +119,17 @@ module RuboCop
118
119
  end
119
120
 
120
121
  def for_assignment_node
121
- return nil unless node.parent&.for_type?
122
+ node.ancestors.find(&:for_type?)
123
+ end
122
124
 
123
- node.parent
125
+ def find_multiple_assignment_node(grandparent_node)
126
+ return unless grandparent_node.type == MULTIPLE_LEFT_HAND_SIDE_TYPE
127
+ return if grandparent_node.children.any?(&:splat_type?)
128
+
129
+ parent = grandparent_node.parent
130
+ return parent if parent.type == MULTIPLE_ASSIGNMENT_TYPE
131
+
132
+ find_multiple_assignment_node(parent)
124
133
  end
125
134
  end
126
135
  end
@@ -6,12 +6,8 @@ module RuboCop
6
6
  # Common methods for finding files.
7
7
  # @api private
8
8
  module FileFinder
9
- def self.root_level=(level)
10
- @root_level = level
11
- end
12
-
13
- def self.root_level?(path, stop_dir)
14
- (@root_level || stop_dir) == path.to_s
9
+ class << self
10
+ attr_accessor :root_level
15
11
  end
16
12
 
17
13
  def find_file_upwards(filename, start_dir, stop_dir = nil)
@@ -34,7 +30,8 @@ module RuboCop
34
30
  file = dir + filename
35
31
  yield(file.to_s) if file.exist?
36
32
 
37
- break if FileFinder.root_level?(dir, stop_dir)
33
+ dir = dir.to_s
34
+ break if dir == stop_dir || dir == FileFinder.root_level
38
35
  end
39
36
  end
40
37
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'base64'
4
3
  require 'cgi'
5
4
  require 'erb'
6
5
  require 'ostruct'
@@ -124,7 +123,10 @@ module RuboCop
124
123
 
125
124
  def base64_encoded_logo_image
126
125
  image = File.read(LOGO_IMAGE_PATH, binmode: true)
127
- Base64.encode64(image)
126
+
127
+ # `Base64.encode64` compatible:
128
+ # https://github.com/ruby/base64/blob/v0.1.1/lib/base64.rb#L27-L40
129
+ [image].pack('m')
128
130
  end
129
131
  end
130
132
  end
@@ -63,7 +63,7 @@ module RuboCop
63
63
 
64
64
  def classname_attribute_value(file)
65
65
  @classname_attribute_value_cache ||= Hash.new do |hash, key|
66
- hash[key] = key.gsub(/\.rb\Z/, '').gsub("#{Dir.pwd}/", '').tr('/', '.')
66
+ hash[key] = key.delete_suffix('.rb').gsub("#{Dir.pwd}/", '').tr('/', '.')
67
67
  end
68
68
  @classname_attribute_value_cache[file]
69
69
  end
@@ -36,12 +36,19 @@ module RuboCop
36
36
  end
37
37
 
38
38
  handle 'initialize' do |request|
39
+ initialization_options = extract_initialization_options_from(request)
40
+
41
+ @server.configure(initialization_options)
42
+
39
43
  @server.write(
40
44
  id: request[:id],
41
45
  result: LanguageServer::Protocol::Interface::InitializeResult.new(
42
46
  capabilities: LanguageServer::Protocol::Interface::ServerCapabilities.new(
43
47
  document_formatting_provider: true,
44
- diagnostic_provider: true,
48
+ diagnostic_provider: LanguageServer::Protocol::Interface::DiagnosticOptions.new(
49
+ inter_file_dependencies: false,
50
+ workspace_diagnostics: false
51
+ ),
45
52
  text_document_sync: LanguageServer::Protocol::Interface::TextDocumentSyncOptions.new(
46
53
  change: LanguageServer::Protocol::Constant::TextDocumentSyncKind::FULL,
47
54
  open_close: true
@@ -108,23 +115,29 @@ module RuboCop
108
115
  end
109
116
 
110
117
  handle 'workspace/executeCommand' do |request|
111
- if request[:params][:command] == 'rubocop.formatAutocorrects'
112
- uri = request[:params][:arguments][0][:uri]
113
- @server.write(
114
- id: request[:id],
115
- method: 'workspace/applyEdit',
116
- params: {
117
- label: 'Format with RuboCop autocorrects',
118
- edit: {
119
- changes: {
120
- uri => format_file(uri)
121
- }
122
- }
123
- }
124
- )
118
+ case (command = request[:params][:command])
119
+ when 'rubocop.formatAutocorrects'
120
+ label = 'Format with RuboCop autocorrects'
121
+ when 'rubocop.formatAutocorrectsAll'
122
+ label = 'Format all with RuboCop autocorrects'
125
123
  else
126
- handle_unsupported_method(request, request[:params][:command])
124
+ handle_unsupported_method(request, command)
125
+ return
127
126
  end
127
+
128
+ uri = request[:params][:arguments][0][:uri]
129
+ @server.write(
130
+ id: request[:id],
131
+ method: 'workspace/applyEdit',
132
+ params: {
133
+ label: label,
134
+ edit: {
135
+ changes: {
136
+ uri => format_file(uri, command: command)
137
+ }
138
+ }
139
+ }
140
+ )
128
141
  end
129
142
 
130
143
  handle 'textDocument/willSave' do |_request|
@@ -162,14 +175,24 @@ module RuboCop
162
175
 
163
176
  private
164
177
 
165
- def format_file(file_uri)
178
+ def extract_initialization_options_from(request)
179
+ safe_autocorrect = request.dig(:params, :initializationOptions, :safeAutocorrect)
180
+
181
+ {
182
+ safe_autocorrect: safe_autocorrect.nil? || safe_autocorrect == true,
183
+ lint_mode: request.dig(:params, :initializationOptions, :lintMode) == true,
184
+ layout_mode: request.dig(:params, :initializationOptions, :layoutMode) == true
185
+ }
186
+ end
187
+
188
+ def format_file(file_uri, command: nil)
166
189
  unless (text = @text_cache[file_uri])
167
190
  Logger.log("Format request arrived before text synchronized; skipping: `#{file_uri}'")
168
191
 
169
192
  return []
170
193
  end
171
194
 
172
- new_text = @server.format(remove_file_protocol_from(file_uri), text)
195
+ new_text = @server.format(remove_file_protocol_from(file_uri), text, command: command)
173
196
 
174
197
  return [] if new_text == text
175
198
 
@@ -14,9 +14,14 @@ module RuboCop
14
14
  # Runtime for Language Server Protocol of RuboCop.
15
15
  # @api private
16
16
  class Runtime
17
+ attr_writer :safe_autocorrect, :lint_mode, :layout_mode
18
+
17
19
  def initialize(config_store)
18
20
  @config_store = config_store
19
21
  @logged_paths = []
22
+ @safe_autocorrect = true
23
+ @lint_mode = false
24
+ @layout_mode = false
20
25
  end
21
26
 
22
27
  # This abuses the `--stdin` option of rubocop and reads the formatted text
@@ -30,10 +35,17 @@ module RuboCop
30
35
  # https://github.com/rubocop/rubocop/blob/v1.52.0/lib/rubocop/cli/command/execute_runner.rb#L95
31
36
  # Setting `parallel: true` would break this here:
32
37
  # https://github.com/rubocop/rubocop/blob/v1.52.0/lib/rubocop/runner.rb#L72
33
- def format(path, text)
38
+ def format(path, text, command:)
39
+ safe_autocorrect = if command
40
+ command == 'rubocop.formatAutocorrects'
41
+ else
42
+ @safe_autocorrect
43
+ end
44
+
34
45
  formatting_options = {
35
- stdin: text, force_exclusion: true, autocorrect: true, safe_autocorrect: true
46
+ stdin: text, force_exclusion: true, autocorrect: true, safe_autocorrect: safe_autocorrect
36
47
  }
48
+ formatting_options[:only] = config_only_options if @lint_mode || @layout_mode
37
49
 
38
50
  redirect_stdout { run_rubocop(formatting_options, path) }
39
51
 
@@ -44,6 +56,7 @@ module RuboCop
44
56
  diagnostic_options = {
45
57
  stdin: text, force_exclusion: true, formatters: ['json'], format: 'json'
46
58
  }
59
+ diagnostic_options[:only] = config_only_options if @lint_mode || @layout_mode
47
60
 
48
61
  json = redirect_stdout { run_rubocop(diagnostic_options, path) }
49
62
  results = JSON.parse(json, symbolize_names: true)
@@ -61,6 +74,13 @@ module RuboCop
61
74
 
62
75
  private
63
76
 
77
+ def config_only_options
78
+ only_options = []
79
+ only_options << 'Lint' if @lint_mode
80
+ only_options << 'Layout' if @layout_mode
81
+ only_options
82
+ end
83
+
64
84
  def redirect_stdout(&block)
65
85
  stdout = StringIO.new
66
86
 
@@ -36,8 +36,8 @@ module RuboCop
36
36
  @routes.handle_unsupported_method(request)
37
37
  end
38
38
  rescue StandardError => e
39
- log("Error #{e.class} #{e.message[0..100]}")
40
- log(e.backtrace.inspect)
39
+ Logger.log("Error #{e.class} #{e.message[0..100]}")
40
+ Logger.log(e.backtrace.inspect)
41
41
  end
42
42
  end
43
43
 
@@ -45,14 +45,20 @@ module RuboCop
45
45
  @writer.write(response)
46
46
  end
47
47
 
48
- def format(path, text)
49
- @runtime.format(path, text)
48
+ def format(path, text, command:)
49
+ @runtime.format(path, text, command: command)
50
50
  end
51
51
 
52
52
  def offenses(path, text)
53
53
  @runtime.offenses(path, text)
54
54
  end
55
55
 
56
+ def configure(options)
57
+ @runtime.safe_autocorrect = options[:safe_autocorrect]
58
+ @runtime.lint_mode = options[:lint_mode]
59
+ @runtime.layout_mode = options[:layout_mode]
60
+ end
61
+
56
62
  def stop(&block)
57
63
  at_exit(&block) if block
58
64
  exit
@@ -7,7 +7,7 @@ module RuboCop
7
7
  class MagicComment
8
8
  # IRB's pattern for matching magic comment tokens.
9
9
  # @see https://github.com/ruby/ruby/blob/b4a55c1/lib/irb/magic-file.rb#L5
10
- TOKEN = /[[:alnum:]\-_]+/.freeze
10
+ TOKEN = '(?<token>[[:alnum:]\-_]+)'
11
11
  KEYWORDS = {
12
12
  encoding: '(?:en)?coding',
13
13
  frozen_string_literal: 'frozen[_-]string[_-]literal',
@@ -129,7 +129,7 @@ module RuboCop
129
129
  # @return [String] if pattern matched
130
130
  # @return [nil] otherwise
131
131
  def extract(pattern)
132
- @comment[pattern, 1]
132
+ @comment[pattern, :token]
133
133
  end
134
134
 
135
135
  # Parent to Vim and Emacs magic comment handling.
@@ -157,10 +157,10 @@ module RuboCop
157
157
  # @return [String] extracted value if it is found
158
158
  # @return [nil] otherwise
159
159
  def match(keyword)
160
- pattern = /\A#{keyword}\s*#{self.class::OPERATOR}\s*(#{TOKEN})\z/
160
+ pattern = /\A#{keyword}\s*#{self.class::OPERATOR}\s*#{TOKEN}\z/
161
161
 
162
162
  tokens.each do |token|
163
- next unless (value = token[pattern, 1])
163
+ next unless (value = token[pattern, :token])
164
164
 
165
165
  return value.downcase
166
166
  end
@@ -188,7 +188,7 @@ module RuboCop
188
188
  # @see https://www.gnu.org/software/emacs/manual/html_node/emacs/Specify-Coding.html
189
189
  # @see https://github.com/ruby/ruby/blob/3f306dc/parse.y#L6873-L6892 Emacs handling in parse.y
190
190
  class EmacsComment < EditorComment
191
- REGEXP = /-\*-(.+)-\*-/.freeze
191
+ REGEXP = /-\*-(?<token>.+)-\*-/.freeze
192
192
  FORMAT = '# -*- %s -*-'
193
193
  SEPARATOR = ';'
194
194
  OPERATOR = ':'
@@ -216,7 +216,7 @@ module RuboCop
216
216
  #
217
217
  # comment.encoding # => 'ascii-8bit'
218
218
  class VimComment < EditorComment
219
- REGEXP = /#\s*vim:\s*(.+)/.freeze
219
+ REGEXP = /#\s*vim:\s*(?<token>.+)/.freeze
220
220
  FORMAT = '# vim: %s'
221
221
  SEPARATOR = ', '
222
222
  OPERATOR = '='
@@ -259,9 +259,11 @@ module RuboCop
259
259
  # comment2.frozen_string_literal # => nil
260
260
  # comment2.encoding # => 'utf-8'
261
261
  class SimpleComment < MagicComment
262
+ FSTRING_LITERAL_COMMENT = 'frozen_string_literal:\s*(true|false)'
263
+
262
264
  # Match `encoding` or `coding`
263
265
  def encoding
264
- extract(/\A\s*\#.*\b#{KEYWORDS[:encoding]}: (#{TOKEN})/io)
266
+ extract(/\A\s*\#\s*(#{FSTRING_LITERAL_COMMENT})?\s*#{KEYWORDS[:encoding]}: (#{TOKEN})/io)
265
267
  end
266
268
 
267
269
  # Rewrite the comment without a given token type
@@ -283,15 +285,15 @@ module RuboCop
283
285
  # Case-insensitive and dashes/underscores are acceptable.
284
286
  # @see https://github.com/ruby/ruby/blob/78b95b4/parse.y#L7134-L7138
285
287
  def extract_frozen_string_literal
286
- extract(/\A\s*#\s*#{KEYWORDS[:frozen_string_literal]}:\s*(#{TOKEN})\s*\z/io)
288
+ extract(/\A\s*#\s*#{KEYWORDS[:frozen_string_literal]}:\s*#{TOKEN}\s*\z/io)
287
289
  end
288
290
 
289
291
  def extract_shareable_constant_value
290
- extract(/\A\s*#\s*#{KEYWORDS[:shareable_constant_value]}:\s*(#{TOKEN})\s*\z/io)
292
+ extract(/\A\s*#\s*#{KEYWORDS[:shareable_constant_value]}:\s*#{TOKEN}\s*\z/io)
291
293
  end
292
294
 
293
295
  def extract_typed
294
- extract(/\A\s*#\s*#{KEYWORDS[:typed]}:\s*(#{TOKEN})\s*\z/io)
296
+ extract(/\A\s*#\s*#{KEYWORDS[:typed]}:\s*#{TOKEN}\s*\z/io)
295
297
  end
296
298
  end
297
299
  end
@@ -202,6 +202,10 @@ module RuboCop
202
202
  lib_root = File.join(File.dirname(__FILE__), '..')
203
203
  exe_root = File.join(lib_root, '..', 'exe')
204
204
 
205
+ # Make sure to use an absolute path to prevent errors on Windows
206
+ # when traversing the relative paths with symlinks.
207
+ exe_root = File.absolute_path(exe_root)
208
+
205
209
  # These are all the files we have `require`d plus everything in the
206
210
  # exe directory. A change to any of them could affect the cop output
207
211
  # so we include them in the cache hash.
@@ -11,6 +11,8 @@ RSpec.shared_context 'isolated environment' do # rubocop:disable Metrics/BlockLe
11
11
  # Make sure to expand all symlinks in the path first. Otherwise we may
12
12
  # get mismatched pathnames when loading config files later on.
13
13
  tmpdir = File.realpath(tmpdir)
14
+ # Make upwards search for .rubocop.yml files stop at this directory.
15
+ RuboCop::FileFinder.root_level = tmpdir
14
16
 
15
17
  virtual_home = File.expand_path(File.join(tmpdir, 'home'))
16
18
  Dir.mkdir(virtual_home)
@@ -21,9 +23,6 @@ RSpec.shared_context 'isolated environment' do # rubocop:disable Metrics/BlockLe
21
23
  root = example.metadata[:root]
22
24
  working_dir = root ? File.join(base_dir, 'work', root) : File.join(base_dir, 'work')
23
25
 
24
- # Make upwards search for .rubocop.yml files stop at this directory.
25
- RuboCop::FileFinder.root_level = working_dir
26
-
27
26
  begin
28
27
  FileUtils.mkdir_p(working_dir)
29
28
 
@@ -421,10 +421,10 @@ module RuboCop
421
421
  end
422
422
 
423
423
  def considered_failure?(offense)
424
- # For :autocorrect level, any offense - corrected or not - is a failure.
425
424
  return false if offense.disabled?
426
425
 
427
- return true if @options[:fail_level] == :autocorrect
426
+ # For :autocorrect level, any correctable offense is a failure, regardless of severity
427
+ return true if @options[:fail_level] == :autocorrect && offense.correctable?
428
428
 
429
429
  !offense.corrected? && offense.severity >= minimum_severity_to_fail
430
430
  end
@@ -461,7 +461,9 @@ module RuboCop
461
461
  @minimum_severity_to_fail ||= begin
462
462
  # Unless given explicitly as `fail_level`, `:info` severity offenses do not fail
463
463
  name = @options[:fail_level] || :refactor
464
- RuboCop::Cop::Severity.new(name)
464
+
465
+ # autocorrect is a fake level - use the default
466
+ RuboCop::Cop::Severity.new(name == :autocorrect ? :refactor : name)
465
467
  end
466
468
  end
467
469
 
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'fileutils'
3
4
  require 'pathname'
4
5
  require_relative '../cache_config'
5
6
  require_relative '../config_finder'
@@ -32,7 +32,7 @@ module RuboCop
32
32
 
33
33
  def ensure_server!
34
34
  if incompatible_version?
35
- puts 'RuboCop version incompatibility found, RuboCop server restarting...'
35
+ warn 'RuboCop version incompatibility found, RuboCop server restarting...'
36
36
  ClientCommand::Stop.new.run
37
37
  elsif check_running_server
38
38
  return
@@ -32,9 +32,9 @@ module RuboCop
32
32
 
33
33
  def interpret_string_escape(escape)
34
34
  case escape[1]
35
- when 'u' then interpret_unicode(escape)
36
- when 'x' then interpret_hex(escape)
37
- when /\d/ then interpret_octal(escape)
35
+ when 'u' then interpret_unicode(escape)
36
+ when 'x' then interpret_hex(escape)
37
+ when /\d/ then interpret_octal(escape)
38
38
  else
39
39
  escape[1] # literal escaped char, like \\
40
40
  end
@@ -94,8 +94,12 @@ module RuboCop
94
94
  end
95
95
 
96
96
  def wanted_dir_patterns(base_dir, exclude_pattern, flags)
97
- base_dir = base_dir.gsub('/{}/', '/\{}/')
98
- dirs = Dir.glob(File.join(base_dir.gsub('/*/', '/\*/').gsub('/**/', '/\**/'), '*/'), flags)
97
+ # Escape glob characters in base_dir to avoid unwanted behavior.
98
+ base_dir = base_dir.gsub(/[\\\{\}\[\]\*\?]/) do |reserved_glob_character|
99
+ "\\#{reserved_glob_character}"
100
+ end
101
+
102
+ dirs = Dir.glob(File.join(base_dir, '*/'), flags)
99
103
  .reject do |dir|
100
104
  next true if dir.end_with?('/./', '/../')
101
105
  next true if File.fnmatch?(exclude_pattern, dir, flags)
@@ -114,7 +118,7 @@ module RuboCop
114
118
  end
115
119
 
116
120
  def combined_exclude_glob_patterns(base_dir)
117
- exclude = @config_store.for(base_dir).for_all_cops['Exclude']
121
+ exclude = @config_store.for(base_dir).for_all_cops['Exclude'] || []
118
122
  patterns = exclude.select { |pattern| pattern.is_a?(String) && pattern.end_with?('/**/*') }
119
123
  .map { |pattern| pattern.sub("#{base_dir}/", '') }
120
124
  "#{base_dir}/{#{patterns.join(',')}}"
@@ -155,9 +155,11 @@ module RuboCop
155
155
  (send _ :required_ruby_version= $_)
156
156
  PATTERN
157
157
 
158
- # @!method gem_requirement?(node)
159
- def_node_matcher :gem_requirement?, <<~PATTERN
160
- (send (const(const _ :Gem):Requirement) :new $str)
158
+ # @!method gem_requirement_versions(node)
159
+ def_node_matcher :gem_requirement_versions, <<~PATTERN
160
+ (send (const(const _ :Gem):Requirement) :new
161
+ {$str+ | (send $str :freeze)+ | (array $str+) | (array (send $str :freeze)+)}
162
+ )
161
163
  PATTERN
162
164
 
163
165
  def name
@@ -194,10 +196,12 @@ module RuboCop
194
196
  end
195
197
 
196
198
  def version_from_right_hand_side(right_hand_side)
199
+ gem_requirement_versions = gem_requirement_versions(right_hand_side)
200
+
197
201
  if right_hand_side.array_type?
198
202
  version_from_array(right_hand_side)
199
- elsif gem_requirement?(right_hand_side)
200
- right_hand_side.children.last.value
203
+ elsif gem_requirement_versions
204
+ gem_requirement_versions.map(&:value)
201
205
  else
202
206
  right_hand_side.value
203
207
  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.53.1'
6
+ STRING = '1.57.2'
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
@@ -162,6 +162,7 @@ require_relative 'rubocop/cop/correctors/string_literal_corrector'
162
162
  require_relative 'rubocop/cop/correctors/unused_arg_corrector'
163
163
 
164
164
  require_relative 'rubocop/cop/bundler/duplicated_gem'
165
+ require_relative 'rubocop/cop/bundler/duplicated_group'
165
166
  require_relative 'rubocop/cop/bundler/gem_comment'
166
167
  require_relative 'rubocop/cop/bundler/gem_filename'
167
168
  require_relative 'rubocop/cop/bundler/gem_version'
@@ -579,6 +580,7 @@ require_relative 'rubocop/cop/style/redundant_regexp_constructor'
579
580
  require_relative 'rubocop/cop/style/redundant_self_assignment'
580
581
  require_relative 'rubocop/cop/style/redundant_self_assignment_branch'
581
582
  require_relative 'rubocop/cop/style/require_order'
583
+ require_relative 'rubocop/cop/style/single_line_do_end_block'
582
584
  require_relative 'rubocop/cop/style/sole_nested_conditional'
583
585
  require_relative 'rubocop/cop/style/static_class'
584
586
  require_relative 'rubocop/cop/style/map_compact_with_conditional_block'