rubocop 1.69.2 → 1.70.0

Sign up to get free protection for your applications and to get access to all the features.
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'