rubocop 1.69.2 → 1.70.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 (102) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +2 -2
  4. data/config/default.yml +19 -2
  5. data/lib/rubocop/cli/command/execute_runner.rb +3 -3
  6. data/lib/rubocop/config.rb +13 -4
  7. data/lib/rubocop/config_loader.rb +4 -0
  8. data/lib/rubocop/config_loader_resolver.rb +14 -3
  9. data/lib/rubocop/config_validator.rb +18 -8
  10. data/lib/rubocop/cop/base.rb +6 -0
  11. data/lib/rubocop/cop/bundler/gem_comment.rb +1 -1
  12. data/lib/rubocop/cop/internal_affairs/cop_enabled.rb +85 -0
  13. data/lib/rubocop/cop/internal_affairs/node_type_predicate.rb +4 -3
  14. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  15. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -7
  16. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -0
  17. data/lib/rubocop/cop/layout/extra_spacing.rb +1 -1
  18. data/lib/rubocop/cop/layout/first_argument_indentation.rb +2 -7
  19. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +2 -7
  20. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +1 -6
  21. data/lib/rubocop/cop/layout/hash_alignment.rb +6 -4
  22. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +1 -0
  23. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +7 -1
  24. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +1 -1
  25. data/lib/rubocop/cop/layout/line_length.rb +1 -0
  26. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +24 -0
  27. data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -1
  28. data/lib/rubocop/cop/layout/space_after_method_name.rb +1 -1
  29. data/lib/rubocop/cop/layout/space_around_operators.rb +3 -3
  30. data/lib/rubocop/cop/layout/trailing_whitespace.rb +5 -3
  31. data/lib/rubocop/cop/lint/constant_reassignment.rb +152 -0
  32. data/lib/rubocop/cop/lint/duplicate_set_element.rb +20 -7
  33. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +11 -3
  34. data/lib/rubocop/cop/lint/nested_method_definition.rb +5 -1
  35. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +4 -3
  36. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +6 -14
  37. data/lib/rubocop/cop/lint/shared_mutable_default.rb +65 -0
  38. data/lib/rubocop/cop/lint/syntax.rb +4 -1
  39. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +1 -4
  40. data/lib/rubocop/cop/lint/void.rb +3 -2
  41. data/lib/rubocop/cop/metrics/method_length.rb +8 -1
  42. data/lib/rubocop/cop/mixin/check_line_breakable.rb +7 -7
  43. data/lib/rubocop/cop/mixin/comments_help.rb +2 -0
  44. data/lib/rubocop/cop/mixin/dig_help.rb +1 -1
  45. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +26 -16
  46. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  47. data/lib/rubocop/cop/mixin/statement_modifier.rb +1 -1
  48. data/lib/rubocop/cop/mixin/string_literals_help.rb +1 -1
  49. data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
  50. data/lib/rubocop/cop/style/access_modifier_declarations.rb +32 -1
  51. data/lib/rubocop/cop/style/and_or.rb +1 -1
  52. data/lib/rubocop/cop/style/arguments_forwarding.rb +1 -4
  53. data/lib/rubocop/cop/style/class_and_module_children.rb +5 -2
  54. data/lib/rubocop/cop/style/each_for_simple_loop.rb +3 -6
  55. data/lib/rubocop/cop/style/empty_else.rb +4 -2
  56. data/lib/rubocop/cop/style/empty_literal.rb +1 -1
  57. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  58. data/lib/rubocop/cop/style/exact_regexp_match.rb +2 -9
  59. data/lib/rubocop/cop/style/exponential_notation.rb +1 -1
  60. data/lib/rubocop/cop/style/float_division.rb +8 -4
  61. data/lib/rubocop/cop/style/hash_except.rb +54 -67
  62. data/lib/rubocop/cop/style/hash_syntax.rb +5 -2
  63. data/lib/rubocop/cop/style/it_assignment.rb +36 -0
  64. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +11 -1
  65. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +2 -0
  66. data/lib/rubocop/cop/style/missing_else.rb +2 -0
  67. data/lib/rubocop/cop/style/multiple_comparison.rb +26 -20
  68. data/lib/rubocop/cop/style/mutable_constant.rb +1 -1
  69. data/lib/rubocop/cop/style/object_then.rb +13 -15
  70. data/lib/rubocop/cop/style/quoted_symbols.rb +1 -1
  71. data/lib/rubocop/cop/style/raise_args.rb +5 -3
  72. data/lib/rubocop/cop/style/random_with_offset.rb +3 -3
  73. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +2 -1
  74. data/lib/rubocop/cop/style/redundant_initialize.rb +12 -3
  75. data/lib/rubocop/cop/style/redundant_line_continuation.rb +7 -3
  76. data/lib/rubocop/cop/style/redundant_parentheses.rb +1 -4
  77. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +3 -0
  78. data/lib/rubocop/cop/style/redundant_self_assignment.rb +6 -5
  79. data/lib/rubocop/cop/style/safe_navigation.rb +1 -1
  80. data/lib/rubocop/cop/style/send_with_literal_method_name.rb +2 -1
  81. data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -2
  82. data/lib/rubocop/cop/style/single_line_methods.rb +2 -3
  83. data/lib/rubocop/cop/style/slicing_with_range.rb +40 -11
  84. data/lib/rubocop/cop/style/super_arguments.rb +63 -15
  85. data/lib/rubocop/cop/style/yoda_condition.rb +8 -4
  86. data/lib/rubocop/cop/style/yoda_expression.rb +1 -0
  87. data/lib/rubocop/cop/util.rb +9 -2
  88. data/lib/rubocop/formatter/formatter_set.rb +1 -1
  89. data/lib/rubocop/lsp/diagnostic.rb +189 -0
  90. data/lib/rubocop/lsp/logger.rb +2 -2
  91. data/lib/rubocop/lsp/routes.rb +7 -23
  92. data/lib/rubocop/lsp/runtime.rb +15 -49
  93. data/lib/rubocop/lsp/stdin_runner.rb +83 -0
  94. data/lib/rubocop/path_util.rb +11 -8
  95. data/lib/rubocop/rspec/shared_contexts.rb +4 -1
  96. data/lib/rubocop/runner.rb +5 -6
  97. data/lib/rubocop/target_ruby.rb +15 -0
  98. data/lib/rubocop/version.rb +1 -1
  99. data/lib/rubocop.rb +3 -0
  100. data/lib/ruby_lsp/rubocop/addon.rb +78 -0
  101. data/lib/ruby_lsp/rubocop/wraps_built_in_lsp_runtime.rb +50 -0
  102. metadata +12 -4
@@ -85,6 +85,12 @@ module RuboCop
85
85
  NONCOMMUTATIVE_OPERATORS = %i[===].freeze
86
86
  PROGRAM_NAMES = %i[$0 $PROGRAM_NAME].freeze
87
87
  RESTRICT_ON_SEND = RuboCop::AST::Node::COMPARISON_OPERATORS
88
+ ENFORCE_YODA_STYLES = %i[
89
+ require_for_all_comparison_operators require_for_equality_operators_only
90
+ ].freeze
91
+ EQUALITY_ONLY_STYLES = %i[
92
+ forbid_for_equality_operators_only require_for_equality_operators_only
93
+ ].freeze
88
94
 
89
95
  # @!method file_constant_equal_program_name?(node)
90
96
  def_node_matcher :file_constant_equal_program_name?, <<~PATTERN
@@ -105,13 +111,11 @@ module RuboCop
105
111
  private
106
112
 
107
113
  def enforce_yoda?
108
- style == :require_for_all_comparison_operators ||
109
- style == :require_for_equality_operators_only
114
+ ENFORCE_YODA_STYLES.include?(style)
110
115
  end
111
116
 
112
117
  def equality_only?
113
- style == :forbid_for_equality_operators_only ||
114
- style == :require_for_equality_operators_only
118
+ EQUALITY_ONLY_STYLES.include?(style)
115
119
  end
116
120
 
117
121
  def yoda_compatible_condition?(node)
@@ -50,6 +50,7 @@ module RuboCop
50
50
 
51
51
  def on_send(node)
52
52
  return unless supported_operators.include?(node.method_name.to_s)
53
+ return unless node.arguments?
53
54
 
54
55
  lhs = node.receiver
55
56
  rhs = node.first_argument
@@ -193,11 +193,18 @@ module RuboCop
193
193
  enforced_style.sub(/^Enforced/, 'Supported').sub('Style', 'Styles')
194
194
  end
195
195
 
196
+ def parse_regexp(text)
197
+ Regexp::Parser.parse(text)
198
+ rescue Regexp::Parser::Error
199
+ # Upon encountering an invalid regular expression,
200
+ # we aim to proceed and identify any remaining potential offenses.
201
+ nil
202
+ end
203
+
196
204
  private
197
205
 
198
206
  def compatible_external_encoding_for?(src)
199
- src = src.dup if RUBY_ENGINE == 'jruby'
200
- src.force_encoding(Encoding.default_external).valid_encoding?
207
+ src.dup.force_encoding(Encoding.default_external).valid_encoding?
201
208
  end
202
209
 
203
210
  def include_or_equal?(source, target)
@@ -77,7 +77,7 @@ module RuboCop
77
77
  case formatter_type
78
78
  when Class
79
79
  formatter_type
80
- when /\A[A-Z]/
80
+ when /\A(::)?[A-Z]/
81
81
  custom_formatter_class(formatter_type)
82
82
  else
83
83
  builtin_formatter_class(formatter_type)
@@ -0,0 +1,189 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'severity'
4
+
5
+ #
6
+ # This code is based on https://github.com/standardrb/standard.
7
+ #
8
+ # Copyright (c) 2023 Test Double, Inc.
9
+ #
10
+ # The MIT License (MIT)
11
+ #
12
+ # https://github.com/standardrb/standard/blob/main/LICENSE.txt
13
+ #
14
+ module RuboCop
15
+ module LSP
16
+ # Diagnostic for Language Server Protocol of RuboCop.
17
+ # @api private
18
+ class Diagnostic
19
+ def initialize(document_encoding, offense, uri, cop_class)
20
+ @document_encoding = document_encoding
21
+ @offense = offense
22
+ @uri = uri
23
+ @cop_class = cop_class
24
+ end
25
+
26
+ def to_lsp_code_actions
27
+ code_actions = []
28
+
29
+ code_actions << autocorrect_action if correctable?
30
+ code_actions << disable_line_action
31
+
32
+ code_actions
33
+ end
34
+
35
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
36
+ def to_lsp_diagnostic(config)
37
+ highlighted = @offense.highlighted_area
38
+
39
+ LanguageServer::Protocol::Interface::Diagnostic.new(
40
+ message: message,
41
+ source: 'RuboCop',
42
+ code: @offense.cop_name,
43
+ code_description: code_description(config),
44
+ severity: severity,
45
+ range: LanguageServer::Protocol::Interface::Range.new(
46
+ start: LanguageServer::Protocol::Interface::Position.new(
47
+ line: @offense.line - 1,
48
+ character: highlighted.begin_pos
49
+ ),
50
+ end: LanguageServer::Protocol::Interface::Position.new(
51
+ line: @offense.line - 1,
52
+ character: highlighted.end_pos
53
+ )
54
+ ),
55
+ data: {
56
+ correctable: correctable?,
57
+ code_actions: to_lsp_code_actions
58
+ }
59
+ )
60
+ end
61
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
62
+
63
+ private
64
+
65
+ def message
66
+ message = @offense.message
67
+ message += "\n\nThis offense is not autocorrectable.\n" unless correctable?
68
+ message
69
+ end
70
+
71
+ def severity
72
+ Severity.find_by(@offense.severity.name)
73
+ end
74
+
75
+ def code_description(config)
76
+ return unless @cop_class
77
+ return unless (doc_url = @cop_class.documentation_url(config))
78
+
79
+ LanguageServer::Protocol::Interface::CodeDescription.new(href: doc_url)
80
+ end
81
+
82
+ # rubocop:disable Layout/LineLength, Metrics/MethodLength
83
+ def autocorrect_action
84
+ LanguageServer::Protocol::Interface::CodeAction.new(
85
+ title: "Autocorrect #{@offense.cop_name}",
86
+ kind: LanguageServer::Protocol::Constant::CodeActionKind::QUICK_FIX,
87
+ edit: LanguageServer::Protocol::Interface::WorkspaceEdit.new(
88
+ document_changes: [
89
+ LanguageServer::Protocol::Interface::TextDocumentEdit.new(
90
+ text_document: LanguageServer::Protocol::Interface::OptionalVersionedTextDocumentIdentifier.new(
91
+ uri: ensure_uri_scheme(@uri.to_s).to_s,
92
+ version: nil
93
+ ),
94
+ edits: correctable? ? offense_replacements : []
95
+ )
96
+ ]
97
+ ),
98
+ is_preferred: true
99
+ )
100
+ end
101
+ # rubocop:enable Layout/LineLength, Metrics/MethodLength
102
+
103
+ # rubocop:disable Metrics/MethodLength
104
+ def offense_replacements
105
+ @offense.corrector.as_replacements.map do |range, replacement|
106
+ LanguageServer::Protocol::Interface::TextEdit.new(
107
+ range: LanguageServer::Protocol::Interface::Range.new(
108
+ start: LanguageServer::Protocol::Interface::Position.new(
109
+ line: range.line - 1,
110
+ character: range.column
111
+ ),
112
+ end: LanguageServer::Protocol::Interface::Position.new(
113
+ line: range.last_line - 1,
114
+ character: range.last_column
115
+ )
116
+ ),
117
+ new_text: replacement
118
+ )
119
+ end
120
+ end
121
+ # rubocop:enable Metrics/MethodLength
122
+
123
+ # rubocop:disable Layout/LineLength, Metrics/MethodLength
124
+ def disable_line_action
125
+ LanguageServer::Protocol::Interface::CodeAction.new(
126
+ title: "Disable #{@offense.cop_name} for this line",
127
+ kind: LanguageServer::Protocol::Constant::CodeActionKind::QUICK_FIX,
128
+ edit: LanguageServer::Protocol::Interface::WorkspaceEdit.new(
129
+ document_changes: [
130
+ LanguageServer::Protocol::Interface::TextDocumentEdit.new(
131
+ text_document: LanguageServer::Protocol::Interface::OptionalVersionedTextDocumentIdentifier.new(
132
+ uri: ensure_uri_scheme(@uri.to_s).to_s,
133
+ version: nil
134
+ ),
135
+ edits: line_disable_comment
136
+ )
137
+ ]
138
+ )
139
+ )
140
+ end
141
+ # rubocop:enable Layout/LineLength, Metrics/MethodLength
142
+
143
+ def line_disable_comment
144
+ new_text = if @offense.source_line.include?(' # rubocop:disable ')
145
+ ",#{@offense.cop_name}"
146
+ else
147
+ " # rubocop:disable #{@offense.cop_name}"
148
+ end
149
+
150
+ eol = LanguageServer::Protocol::Interface::Position.new(
151
+ line: @offense.line - 1,
152
+ character: length_of_line(@offense.source_line)
153
+ )
154
+
155
+ # TODO: fails for multiline strings - may be preferable to use block
156
+ # comments to disable some offenses
157
+ inline_comment = LanguageServer::Protocol::Interface::TextEdit.new(
158
+ range: LanguageServer::Protocol::Interface::Range.new(start: eol, end: eol),
159
+ new_text: new_text
160
+ )
161
+
162
+ [inline_comment]
163
+ end
164
+
165
+ def length_of_line(line)
166
+ if @document_encoding == Encoding::UTF_16LE
167
+ line_length = 0
168
+ line.codepoints.each do |codepoint|
169
+ line_length += 1
170
+ line_length += 1 if codepoint > RubyLsp::Document::Scanner::SURROGATE_PAIR_START
171
+ end
172
+ line_length
173
+ else
174
+ line.length
175
+ end
176
+ end
177
+
178
+ def correctable?
179
+ !@offense.corrector.nil?
180
+ end
181
+
182
+ def ensure_uri_scheme(uri)
183
+ uri = URI.parse(uri)
184
+ uri.scheme = 'file' if uri.scheme.nil?
185
+ uri
186
+ end
187
+ end
188
+ end
189
+ end
@@ -14,8 +14,8 @@ module RuboCop
14
14
  # Log for Language Server Protocol of RuboCop.
15
15
  # @api private
16
16
  class Logger
17
- def self.log(message)
18
- warn("[server] #{message}")
17
+ def self.log(message, prefix: '[server]')
18
+ warn("#{prefix} #{message}")
19
19
  end
20
20
  end
21
21
  end
@@ -16,6 +16,11 @@ module RuboCop
16
16
  # Routes for Language Server Protocol of RuboCop.
17
17
  # @api private
18
18
  class Routes
19
+ CONFIGURATION_FILE_PATTERNS = [
20
+ RuboCop::ConfigFinder::DOTFILE,
21
+ RuboCop::CLI::Command::AutoGenerateConfig::AUTO_GENERATED_FILE
22
+ ].freeze
23
+
19
24
  def self.handle(name, &block)
20
25
  define_method(:"handle_#{name}", &block)
21
26
  end
@@ -96,7 +101,7 @@ module RuboCop
96
101
 
97
102
  handle 'workspace/didChangeWatchedFiles' do |request|
98
103
  changed = request[:params][:changes].any? do |change|
99
- change[:uri].end_with?(RuboCop::ConfigFinder::DOTFILE)
104
+ CONFIGURATION_FILE_PATTERNS.any? { |path| change[:uri].end_with?(path) }
100
105
  end
101
106
 
102
107
  if changed
@@ -204,14 +209,12 @@ module RuboCop
204
209
 
205
210
  def diagnostic(file_uri, text)
206
211
  @text_cache[file_uri] = text
207
- offenses = @server.offenses(remove_file_protocol_from(file_uri), text)
208
- diagnostics = offenses.map { |offense| to_diagnostic(offense) }
209
212
 
210
213
  {
211
214
  method: 'textDocument/publishDiagnostics',
212
215
  params: {
213
216
  uri: file_uri,
214
- diagnostics: diagnostics
217
+ diagnostics: @server.offenses(remove_file_protocol_from(file_uri), text)
215
218
  }
216
219
  }
217
220
  end
@@ -219,25 +222,6 @@ module RuboCop
219
222
  def remove_file_protocol_from(uri)
220
223
  uri.delete_prefix('file://')
221
224
  end
222
-
223
- def to_diagnostic(offense)
224
- code = offense[:cop_name]
225
- message = offense[:message]
226
- loc = offense[:location]
227
- rubocop_severity = offense[:severity]
228
- severity = Severity.find_by(rubocop_severity)
229
-
230
- {
231
- code: code, message: message, range: to_range(loc), severity: severity, source: 'rubocop'
232
- }
233
- end
234
-
235
- def to_range(location)
236
- {
237
- start: { character: location[:start_column] - 1, line: location[:start_line] - 1 },
238
- end: { character: location[:last_column], line: location[:last_line] - 1 }
239
- }
240
- end
241
225
  end
242
226
  end
243
227
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'stringio'
3
+ require_relative 'diagnostic'
4
+ require_relative 'stdin_runner'
4
5
 
5
6
  #
6
7
  # This code is based on https://github.com/standardrb/standard.
@@ -19,24 +20,14 @@ module RuboCop
19
20
  attr_writer :safe_autocorrect, :lint_mode, :layout_mode
20
21
 
21
22
  def initialize(config_store)
22
- @config_store = config_store
23
- @logged_paths = []
23
+ @runner = RuboCop::Lsp::StdinRunner.new(config_store)
24
+ @cop_registry = RuboCop::Cop::Registry.global.to_h
25
+
24
26
  @safe_autocorrect = true
25
27
  @lint_mode = false
26
28
  @layout_mode = false
27
29
  end
28
30
 
29
- # This abuses the `--stdin` option of rubocop and reads the formatted text
30
- # from the `options[:stdin]` that rubocop mutates. This depends on
31
- # `parallel: false` as well as the fact that RuboCop doesn't otherwise dup
32
- # or reassign that options object. Risky business!
33
- #
34
- # Reassigning `options[:stdin]` is done here:
35
- # https://github.com/rubocop/rubocop/blob/v1.52.0/lib/rubocop/cop/team.rb#L131
36
- # Printing `options[:stdin]`
37
- # https://github.com/rubocop/rubocop/blob/v1.52.0/lib/rubocop/cli/command/execute_runner.rb#L95
38
- # Setting `parallel: true` would break this here:
39
- # https://github.com/rubocop/rubocop/blob/v1.52.0/lib/rubocop/runner.rb#L72
40
31
  def format(path, text, command:)
41
32
  safe_autocorrect = if command
42
33
  command == 'rubocop.formatAutocorrects'
@@ -44,34 +35,23 @@ module RuboCop
44
35
  @safe_autocorrect
45
36
  end
46
37
 
47
- formatting_options = {
48
- stdin: text, force_exclusion: true, autocorrect: true, safe_autocorrect: safe_autocorrect
49
- }
38
+ formatting_options = { autocorrect: true, safe_autocorrect: safe_autocorrect }
50
39
  formatting_options[:only] = config_only_options if @lint_mode || @layout_mode
51
40
 
52
- redirect_stdout { run_rubocop(formatting_options, path) }
53
-
54
- formatting_options[:stdin]
41
+ @runner.run(path, text, formatting_options)
42
+ @runner.formatted_source
55
43
  end
56
44
 
57
- def offenses(path, text)
58
- diagnostic_options = {
59
- stdin: text, force_exclusion: true, formatters: ['json'], format: 'json'
60
- }
45
+ def offenses(path, text, document_encoding = nil)
46
+ diagnostic_options = {}
61
47
  diagnostic_options[:only] = config_only_options if @lint_mode || @layout_mode
62
48
 
63
- json = redirect_stdout { run_rubocop(diagnostic_options, path) }
64
- results = JSON.parse(json, symbolize_names: true)
65
-
66
- if results[:files].empty?
67
- unless @logged_paths.include?(path)
68
- Logger.log "Ignoring file, per configuration: #{path}"
69
- @logged_paths << path
70
- end
71
- return []
49
+ @runner.run(path, text, diagnostic_options)
50
+ @runner.offenses.map do |offense|
51
+ Diagnostic.new(
52
+ document_encoding, offense, path, @cop_registry[offense.cop_name]&.first
53
+ ).to_lsp_diagnostic(@runner.config_for_working_directory)
72
54
  end
73
-
74
- results.dig(:files, 0, :offenses)
75
55
  end
76
56
 
77
57
  private
@@ -82,20 +62,6 @@ module RuboCop
82
62
  only_options << 'Layout' if @layout_mode
83
63
  only_options
84
64
  end
85
-
86
- def redirect_stdout(&block)
87
- stdout = StringIO.new
88
-
89
- RuboCop::Server::Helper.redirect(stdout: stdout, &block)
90
-
91
- stdout.string
92
- end
93
-
94
- def run_rubocop(options, path)
95
- runner = RuboCop::Runner.new(options, @config_store)
96
-
97
- runner.run([path])
98
- end
99
65
  end
100
66
  end
101
67
  end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # This code is based on https://github.com/standardrb/standard.
5
+ #
6
+ # Copyright (c) 2023 Test Double, Inc.
7
+ #
8
+ # The MIT License (MIT)
9
+ #
10
+ # https://github.com/standardrb/standard/blob/main/LICENSE.txt
11
+ #
12
+ module RuboCop
13
+ module Lsp
14
+ # Originally lifted from:
15
+ # https://github.com/Shopify/ruby-lsp/blob/8d4c17efce4e8ecc8e7c557ab2981db6b22c0b6d/lib/ruby_lsp/requests/support/rubocop_runner.rb#L20
16
+ # @api private
17
+ class StdinRunner < RuboCop::Runner
18
+ class ConfigurationError < StandardError; end
19
+
20
+ attr_reader :offenses, :config_for_working_directory
21
+
22
+ DEFAULT_RUBOCOP_OPTIONS = {
23
+ stderr: true,
24
+ force_exclusion: true,
25
+ formatters: ['RuboCop::Formatter::BaseFormatter'],
26
+ raise_cop_error: true,
27
+ todo_file: nil,
28
+ todo_ignore_files: []
29
+ }.freeze
30
+
31
+ def initialize(config_store)
32
+ @options = {}
33
+
34
+ @offenses = []
35
+ @warnings = []
36
+ @errors = []
37
+
38
+ @config_for_working_directory = config_store.for_pwd
39
+
40
+ super(@options, config_store)
41
+ end
42
+
43
+ # rubocop:disable Metrics/MethodLength
44
+ def run(path, contents, options)
45
+ @options = options.merge(DEFAULT_RUBOCOP_OPTIONS)
46
+ @options[:stdin] = contents
47
+
48
+ @offenses = []
49
+ @warnings = []
50
+ @errors = []
51
+
52
+ super([path])
53
+
54
+ raise Interrupt if aborting?
55
+ rescue RuboCop::Runner::InfiniteCorrectionLoop => e
56
+ if defined?(::RubyLsp::Requests::Formatting::Error)
57
+ raise ::RubyLsp::Requests::Formatting::Error, e.message
58
+ end
59
+
60
+ raise e
61
+ rescue RuboCop::ValidationError => e
62
+ raise ConfigurationError, e.message
63
+ rescue StandardError => e
64
+ if defined?(::RubyLsp::Requests::Formatting::Error)
65
+ raise ::RubyLsp::Requests::Support::InternalRuboCopError, e
66
+ end
67
+
68
+ raise e
69
+ end
70
+ # rubocop:enable Metrics/MethodLength
71
+
72
+ def formatted_source
73
+ @options[:stdin]
74
+ end
75
+
76
+ private
77
+
78
+ def file_finished(_file, offenses)
79
+ @offenses = offenses
80
+ end
81
+ end
82
+ end
83
+ end
@@ -32,16 +32,19 @@ module RuboCop
32
32
  private_constant :SMART_PATH_CACHE
33
33
 
34
34
  def smart_path(path)
35
- SMART_PATH_CACHE[path] ||= begin
36
- # Ideally, we calculate this relative to the project root.
37
- base_dir = Dir.pwd
38
-
39
- if path.start_with? base_dir
40
- relative_path(path, base_dir)
35
+ SMART_PATH_CACHE[path] ||=
36
+ if path.is_a?(RemoteConfig)
37
+ path.uri.to_s
41
38
  else
42
- path
39
+ # Ideally, we calculate this relative to the project root.
40
+ base_dir = Dir.pwd
41
+
42
+ if path.start_with? base_dir
43
+ relative_path(path, base_dir)
44
+ else
45
+ path
46
+ end
43
47
  end
44
- end
45
48
  end
46
49
 
47
50
  # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
@@ -98,6 +98,8 @@ RSpec.shared_context 'config' do # rubocop:disable Metrics/BlockLength
98
98
 
99
99
  let(:cop_options) { {} }
100
100
 
101
+ let(:gem_versions) { {} }
102
+
101
103
  ### Utilities
102
104
 
103
105
  def source_range(range, buffer: source_buffer)
@@ -138,7 +140,8 @@ RSpec.shared_context 'config' do # rubocop:disable Metrics/BlockLength
138
140
 
139
141
  allow(config).to receive(:gem_versions_in_target).and_return(
140
142
  {
141
- 'railties' => rails_version_in_gemfile
143
+ 'railties' => rails_version_in_gemfile,
144
+ **gem_versions.transform_values { |value| Gem::Version.new(value) }
142
145
  }
143
146
  )
144
147
 
@@ -20,11 +20,7 @@ module RuboCop
20
20
  message = 'Infinite loop detected'
21
21
  message += " in #{path}" if path
22
22
  message += " and caused by #{root_cause}" if root_cause
23
- message += "\n"
24
- hint = 'Hint: Please update to the latest RuboCop version if not already in use, ' \
25
- "and report a bug if the issue still occurs on this version.\n" \
26
- 'Please check the latest version at https://rubygems.org/gems/rubocop.'
27
- super(Rainbow(message).red + Rainbow(hint).yellow)
23
+ super(message)
28
24
  end
29
25
  end
30
26
 
@@ -157,8 +153,11 @@ module RuboCop
157
153
  file_started(file)
158
154
  offenses = file_offenses(file)
159
155
  rescue InfiniteCorrectionLoop => e
156
+ raise e if @options[:raise_cop_error]
157
+
158
+ errors << e
159
+ warn Rainbow(e.message).red
160
160
  offenses = e.offenses.compact.sort.freeze
161
- raise
162
161
  ensure
163
162
  file_finished(file, offenses || [])
164
163
  end
@@ -34,6 +34,20 @@ module RuboCop
34
34
  end
35
35
  end
36
36
 
37
+ # The target ruby version may be configured by setting the
38
+ # `RUBOCOP_TARGET_RUBY_VERSION` environment variable.
39
+ class RuboCopEnvVar < Source
40
+ def name
41
+ '`RUBOCOP_TARGET_RUBY_VERSION` environment variable'
42
+ end
43
+
44
+ private
45
+
46
+ def find_version
47
+ ENV.fetch('RUBOCOP_TARGET_RUBY_VERSION', nil)&.to_f
48
+ end
49
+ end
50
+
37
51
  # The target ruby version may be configured in RuboCop's config.
38
52
  # @api private
39
53
  class RuboCopConfig < Source
@@ -246,6 +260,7 @@ module RuboCop
246
260
  end
247
261
 
248
262
  SOURCES = [
263
+ RuboCopEnvVar,
249
264
  RuboCopConfig,
250
265
  GemspecFile,
251
266
  RubyVersionFile,
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '1.69.2'
6
+ STRING = '1.70.0'
7
7
 
8
8
  MSG = '%<version>s (using %<parser_version>s, ' \
9
9
  'rubocop-ast %<rubocop_ast_version>s, ' \
data/lib/rubocop.rb CHANGED
@@ -300,6 +300,7 @@ require_relative 'rubocop/cop/lint/boolean_symbol'
300
300
  require_relative 'rubocop/cop/lint/circular_argument_reference'
301
301
  require_relative 'rubocop/cop/lint/constant_definition_in_block'
302
302
  require_relative 'rubocop/cop/lint/constant_overwritten_in_rescue'
303
+ require_relative 'rubocop/cop/lint/constant_reassignment'
303
304
  require_relative 'rubocop/cop/lint/constant_resolution'
304
305
  require_relative 'rubocop/cop/lint/debugger'
305
306
  require_relative 'rubocop/cop/lint/deprecated_class_methods'
@@ -329,6 +330,7 @@ require_relative 'rubocop/cop/lint/empty_in_pattern'
329
330
  require_relative 'rubocop/cop/lint/empty_interpolation'
330
331
  require_relative 'rubocop/cop/lint/empty_when'
331
332
  require_relative 'rubocop/cop/lint/ensure_return'
333
+ require_relative 'rubocop/cop/lint/shared_mutable_default'
332
334
  require_relative 'rubocop/cop/lint/erb_new_arguments'
333
335
  require_relative 'rubocop/cop/lint/flip_flop'
334
336
  require_relative 'rubocop/cop/lint/float_comparison'
@@ -573,6 +575,7 @@ require_relative 'rubocop/cop/style/inverse_methods'
573
575
  require_relative 'rubocop/cop/style/inline_comment'
574
576
  require_relative 'rubocop/cop/style/invertible_unless_condition'
575
577
  require_relative 'rubocop/cop/style/ip_addresses'
578
+ require_relative 'rubocop/cop/style/it_assignment'
576
579
  require_relative 'rubocop/cop/style/keyword_arguments_merging'
577
580
  require_relative 'rubocop/cop/style/keyword_parameters_order'
578
581
  require_relative 'rubocop/cop/style/lambda'