rubocop 1.69.2 → 1.71.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (160) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +2 -2
  4. data/config/default.yml +36 -2
  5. data/lib/rubocop/cli/command/execute_runner.rb +3 -3
  6. data/lib/rubocop/cli/command/show_cops.rb +24 -2
  7. data/lib/rubocop/comment_config.rb +1 -1
  8. data/lib/rubocop/config.rb +13 -4
  9. data/lib/rubocop/config_loader.rb +4 -0
  10. data/lib/rubocop/config_loader_resolver.rb +14 -3
  11. data/lib/rubocop/config_validator.rb +18 -8
  12. data/lib/rubocop/cop/base.rb +6 -0
  13. data/lib/rubocop/cop/bundler/gem_comment.rb +1 -1
  14. data/lib/rubocop/cop/internal_affairs/cop_enabled.rb +85 -0
  15. data/lib/rubocop/cop/internal_affairs/location_expression.rb +2 -1
  16. data/lib/rubocop/cop/internal_affairs/node_first_or_last_argument.rb +3 -2
  17. data/lib/rubocop/cop/internal_affairs/node_type_predicate.rb +4 -3
  18. data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +90 -0
  19. data/lib/rubocop/cop/internal_affairs/redundant_source_range.rb +2 -1
  20. data/lib/rubocop/cop/internal_affairs/single_line_comparison.rb +5 -4
  21. data/lib/rubocop/cop/internal_affairs.rb +2 -0
  22. data/lib/rubocop/cop/layout/argument_alignment.rb +2 -8
  23. data/lib/rubocop/cop/layout/class_structure.rb +7 -7
  24. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -0
  25. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +1 -1
  26. data/lib/rubocop/cop/layout/extra_spacing.rb +1 -1
  27. data/lib/rubocop/cop/layout/first_argument_indentation.rb +2 -7
  28. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +2 -7
  29. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +2 -7
  30. data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +1 -1
  31. data/lib/rubocop/cop/layout/hash_alignment.rb +6 -4
  32. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +1 -0
  33. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +7 -1
  34. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +1 -1
  35. data/lib/rubocop/cop/layout/line_length.rb +1 -0
  36. data/lib/rubocop/cop/layout/multiline_hash_key_line_breaks.rb +1 -1
  37. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +25 -0
  38. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +1 -0
  39. data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -1
  40. data/lib/rubocop/cop/layout/space_after_method_name.rb +1 -1
  41. data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -0
  42. data/lib/rubocop/cop/layout/space_around_operators.rb +3 -3
  43. data/lib/rubocop/cop/layout/trailing_whitespace.rb +5 -3
  44. data/lib/rubocop/cop/lint/array_literal_in_regexp.rb +119 -0
  45. data/lib/rubocop/cop/lint/constant_reassignment.rb +152 -0
  46. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +1 -1
  47. data/lib/rubocop/cop/lint/duplicate_set_element.rb +20 -7
  48. data/lib/rubocop/cop/lint/float_comparison.rb +5 -2
  49. data/lib/rubocop/cop/lint/float_out_of_range.rb +1 -1
  50. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +23 -5
  51. data/lib/rubocop/cop/lint/mixed_case_range.rb +1 -1
  52. data/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb +1 -1
  53. data/lib/rubocop/cop/lint/nested_method_definition.rb +5 -1
  54. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +4 -3
  55. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +17 -30
  56. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +2 -1
  57. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
  58. data/lib/rubocop/cop/lint/redundant_string_coercion.rb +2 -2
  59. data/lib/rubocop/cop/lint/rescue_exception.rb +1 -1
  60. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +7 -0
  61. data/lib/rubocop/cop/lint/shared_mutable_default.rb +65 -0
  62. data/lib/rubocop/cop/lint/syntax.rb +4 -1
  63. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +1 -4
  64. data/lib/rubocop/cop/lint/useless_assignment.rb +1 -1
  65. data/lib/rubocop/cop/lint/useless_numeric_operation.rb +2 -1
  66. data/lib/rubocop/cop/lint/void.rb +3 -2
  67. data/lib/rubocop/cop/metrics/collection_literal_length.rb +7 -0
  68. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +1 -1
  69. data/lib/rubocop/cop/metrics/method_length.rb +8 -1
  70. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
  71. data/lib/rubocop/cop/mixin/check_line_breakable.rb +7 -7
  72. data/lib/rubocop/cop/mixin/comments_help.rb +2 -0
  73. data/lib/rubocop/cop/mixin/dig_help.rb +1 -1
  74. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -1
  75. data/lib/rubocop/cop/mixin/hash_subset.rb +170 -0
  76. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +26 -16
  77. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  78. data/lib/rubocop/cop/mixin/statement_modifier.rb +8 -3
  79. data/lib/rubocop/cop/mixin/string_literals_help.rb +1 -1
  80. data/lib/rubocop/cop/mixin/trailing_comma.rb +2 -2
  81. data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
  82. data/lib/rubocop/cop/security/compound_hash.rb +1 -0
  83. data/lib/rubocop/cop/style/access_modifier_declarations.rb +32 -1
  84. data/lib/rubocop/cop/style/and_or.rb +1 -1
  85. data/lib/rubocop/cop/style/arguments_forwarding.rb +1 -4
  86. data/lib/rubocop/cop/style/array_first_last.rb +18 -2
  87. data/lib/rubocop/cop/style/block_delimiters.rb +6 -19
  88. data/lib/rubocop/cop/style/class_and_module_children.rb +5 -2
  89. data/lib/rubocop/cop/style/collection_methods.rb +1 -1
  90. data/lib/rubocop/cop/style/conditional_assignment.rb +3 -1
  91. data/lib/rubocop/cop/style/each_for_simple_loop.rb +4 -7
  92. data/lib/rubocop/cop/style/empty_else.rb +4 -2
  93. data/lib/rubocop/cop/style/empty_literal.rb +1 -1
  94. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  95. data/lib/rubocop/cop/style/exact_regexp_match.rb +3 -10
  96. data/lib/rubocop/cop/style/exponential_notation.rb +1 -1
  97. data/lib/rubocop/cop/style/fetch_env_var.rb +1 -1
  98. data/lib/rubocop/cop/style/float_division.rb +8 -4
  99. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +1 -1
  100. data/lib/rubocop/cop/style/hash_each_methods.rb +1 -1
  101. data/lib/rubocop/cop/style/hash_except.rb +9 -148
  102. data/lib/rubocop/cop/style/hash_slice.rb +65 -0
  103. data/lib/rubocop/cop/style/hash_syntax.rb +5 -2
  104. data/lib/rubocop/cop/style/it_assignment.rb +36 -0
  105. data/lib/rubocop/cop/style/map_to_set.rb +3 -2
  106. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +11 -1
  107. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +2 -0
  108. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +2 -1
  109. data/lib/rubocop/cop/style/missing_else.rb +2 -0
  110. data/lib/rubocop/cop/style/multiple_comparison.rb +26 -20
  111. data/lib/rubocop/cop/style/mutable_constant.rb +2 -2
  112. data/lib/rubocop/cop/style/object_then.rb +13 -15
  113. data/lib/rubocop/cop/style/open_struct_use.rb +4 -4
  114. data/lib/rubocop/cop/style/quoted_symbols.rb +1 -1
  115. data/lib/rubocop/cop/style/raise_args.rb +6 -4
  116. data/lib/rubocop/cop/style/random_with_offset.rb +3 -3
  117. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +2 -1
  118. data/lib/rubocop/cop/style/redundant_exception.rb +1 -1
  119. data/lib/rubocop/cop/style/redundant_freeze.rb +1 -1
  120. data/lib/rubocop/cop/style/redundant_initialize.rb +12 -3
  121. data/lib/rubocop/cop/style/redundant_line_continuation.rb +34 -13
  122. data/lib/rubocop/cop/style/redundant_parentheses.rb +4 -4
  123. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +3 -0
  124. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +1 -1
  125. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +1 -1
  126. data/lib/rubocop/cop/style/redundant_self_assignment.rb +6 -5
  127. data/lib/rubocop/cop/style/safe_navigation.rb +1 -1
  128. data/lib/rubocop/cop/style/send_with_literal_method_name.rb +2 -1
  129. data/lib/rubocop/cop/style/single_line_block_params.rb +1 -1
  130. data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -2
  131. data/lib/rubocop/cop/style/single_line_methods.rb +2 -3
  132. data/lib/rubocop/cop/style/slicing_with_range.rb +40 -11
  133. data/lib/rubocop/cop/style/sole_nested_conditional.rb +1 -1
  134. data/lib/rubocop/cop/style/string_methods.rb +1 -1
  135. data/lib/rubocop/cop/style/super_arguments.rb +63 -15
  136. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +4 -1
  137. data/lib/rubocop/cop/style/yoda_condition.rb +8 -4
  138. data/lib/rubocop/cop/style/yoda_expression.rb +1 -0
  139. data/lib/rubocop/cop/util.rb +9 -2
  140. data/lib/rubocop/cops_documentation_generator.rb +13 -13
  141. data/lib/rubocop/directive_comment.rb +9 -8
  142. data/lib/rubocop/formatter/formatter_set.rb +1 -1
  143. data/lib/rubocop/lsp/diagnostic.rb +189 -0
  144. data/lib/rubocop/lsp/logger.rb +2 -2
  145. data/lib/rubocop/lsp/routes.rb +7 -23
  146. data/lib/rubocop/lsp/runtime.rb +15 -49
  147. data/lib/rubocop/lsp/stdin_runner.rb +83 -0
  148. data/lib/rubocop/options.rb +2 -1
  149. data/lib/rubocop/path_util.rb +11 -8
  150. data/lib/rubocop/result_cache.rb +13 -13
  151. data/lib/rubocop/rspec/expect_offense.rb +6 -2
  152. data/lib/rubocop/rspec/shared_contexts.rb +4 -1
  153. data/lib/rubocop/runner.rb +5 -6
  154. data/lib/rubocop/target_finder.rb +1 -0
  155. data/lib/rubocop/target_ruby.rb +15 -0
  156. data/lib/rubocop/version.rb +1 -1
  157. data/lib/rubocop.rb +6 -0
  158. data/lib/ruby_lsp/rubocop/addon.rb +78 -0
  159. data/lib/ruby_lsp/rubocop/wraps_built_in_lsp_runtime.rb +50 -0
  160. metadata +17 -8
@@ -23,6 +23,10 @@ module RuboCop
23
23
  # `define_method`, therefore, `super` used within these blocks will be allowed.
24
24
  # This approach might result in false negatives, yet ensuring safe detection takes precedence.
25
25
  #
26
+ # NOTE: When forwarding the same arguments but replacing the block argument with a new inline
27
+ # block, it is not necessary to explicitly list the non-block arguments. As such, an offense
28
+ # will be registered in this case.
29
+ #
26
30
  # @example
27
31
  # # bad
28
32
  # def method(*args, **kwargs)
@@ -44,6 +48,16 @@ module RuboCop
44
48
  # super()
45
49
  # end
46
50
  #
51
+ # # bad - forwarding with overridden block
52
+ # def method(*args, **kwargs, &block)
53
+ # super(*args, **kwargs) { do_something }
54
+ # end
55
+ #
56
+ # # good - implicitly passing all non-block arguments
57
+ # def method(*args, **kwargs, &block)
58
+ # super { do_something }
59
+ # end
60
+ #
47
61
  # # good - assigning to the block variable before calling super
48
62
  # def method(&block)
49
63
  # # Assigning to the block variable would pass the old value to super,
@@ -58,44 +72,75 @@ module RuboCop
58
72
  ASSIGN_TYPES = %i[or_asgn lvasgn].freeze
59
73
 
60
74
  MSG = 'Call `super` without arguments and parentheses when the signature is identical.'
75
+ MSG_INLINE_BLOCK = 'Call `super` without arguments and parentheses when all positional ' \
76
+ 'and keyword arguments are forwarded.'
61
77
 
62
78
  def on_super(super_node)
63
- def_node = super_node.ancestors.find do |node|
79
+ return unless (def_node = find_def_node(super_node))
80
+
81
+ def_node_args = def_node.arguments.argument_list
82
+ super_args = preprocess_super_args(super_node.arguments)
83
+
84
+ return unless arguments_identical?(def_node, super_node, def_node_args, super_args)
85
+
86
+ # If the number of arguments to the def node and super node are different here,
87
+ # it's because the block argument is not forwarded.
88
+ message = def_node_args.size == super_args.size ? MSG : MSG_INLINE_BLOCK
89
+ add_offense(super_node, message: message) do |corrector|
90
+ corrector.replace(super_node, 'super')
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ def find_def_node(super_node)
97
+ super_node.ancestors.find do |node|
64
98
  # When defining dynamic methods, implicitly calling `super` is not possible.
65
99
  # Since there is a possibility of delegation to `define_method`,
66
100
  # `super` used within the block is always allowed.
67
- break if node.block_type?
101
+ break if node.block_type? && !block_sends_to_super?(super_node, node)
68
102
 
69
103
  break node if DEF_TYPES.include?(node.type)
70
104
  end
71
- return unless def_node
72
- return unless arguments_identical?(def_node, def_node.arguments.argument_list,
73
- super_node.arguments)
74
-
75
- add_offense(super_node) { |corrector| corrector.replace(super_node, 'super') }
76
105
  end
77
106
 
78
- private
79
-
80
107
  # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
81
- def arguments_identical?(def_node, def_args, super_args)
82
- super_args = preprocess_super_args(super_args)
83
- return false if def_args.size != super_args.size
108
+ def arguments_identical?(def_node, super_node, def_args, super_args)
109
+ return false if argument_list_size_differs?(def_args, super_args, super_node)
84
110
 
85
111
  def_args.zip(super_args).each do |def_arg, super_arg|
86
112
  next if positional_arg_same?(def_arg, super_arg)
87
113
  next if positional_rest_arg_same(def_arg, super_arg)
88
114
  next if keyword_arg_same?(def_arg, super_arg)
89
115
  next if keyword_rest_arg_same?(def_arg, super_arg)
90
- next if block_arg_same?(def_node, def_arg, super_arg)
116
+ next if block_arg_same?(def_node, super_node, def_arg, super_arg)
91
117
  next if forward_arg_same?(def_arg, super_arg)
92
118
 
93
119
  return false
94
120
  end
121
+
95
122
  true
96
123
  end
97
124
  # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
98
125
 
126
+ def argument_list_size_differs?(def_args, super_args, super_node)
127
+ # If the def node has a block argument and the super node has an explicit block,
128
+ # the number of arguments is the same, so ignore the def node block arg.
129
+ def_args_size = def_args.size
130
+ def_args_size -= 1 if def_args.any?(&:blockarg_type?) && block_sends_to_super?(super_node)
131
+
132
+ def_args_size != super_args.size
133
+ end
134
+
135
+ def block_sends_to_super?(super_node, parent_node = super_node.parent)
136
+ # Checks if the send node of a block is the given super node,
137
+ # or a method chain containing it.
138
+ return false unless parent_node
139
+ return false unless parent_node.type?(:block, :numblock)
140
+
141
+ parent_node.send_node.each_node(:super).any?(super_node)
142
+ end
143
+
99
144
  def positional_arg_same?(def_arg, super_arg)
100
145
  return false unless def_arg.arg_type? || def_arg.optarg_type?
101
146
  return false unless super_arg.lvar_type?
@@ -133,8 +178,11 @@ module RuboCop
133
178
  def_arg.name == lvar_node.children.first
134
179
  end
135
180
 
136
- def block_arg_same?(def_node, def_arg, super_arg)
137
- return false unless def_arg.blockarg_type? && super_arg.block_pass_type?
181
+ def block_arg_same?(def_node, super_node, def_arg, super_arg)
182
+ return false unless def_arg.blockarg_type?
183
+ return true if block_sends_to_super?(super_node)
184
+ return false unless super_arg.block_pass_type?
185
+
138
186
  # anonymous forwarding
139
187
  return true if (block_pass_child = super_arg.children.first).nil? && def_arg.name.nil?
140
188
 
@@ -7,12 +7,15 @@ module RuboCop
7
7
  # The supported styles are:
8
8
  #
9
9
  # * `consistent_comma`: Requires a comma after the last argument,
10
- # for all parenthesized method calls with arguments.
10
+ # for all parenthesized multi-line method calls with arguments.
11
11
  # * `comma`: Requires a comma after the last argument, but only for
12
12
  # parenthesized method calls where each argument is on its own line.
13
13
  # * `no_comma`: Requires that there is no comma after the last
14
14
  # argument.
15
15
  #
16
+ # Regardless of style, trailing commas are not allowed in
17
+ # single-line method calls.
18
+ #
16
19
  # @example EnforcedStyleForMultiline: consistent_comma
17
20
  # # bad
18
21
  # method(1, 2,)
@@ -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)
@@ -18,7 +18,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
18
18
  description: ->(data) { "#{data.description}\n" },
19
19
  safety: ->(data) { safety_object(data.safety_objects, data.cop) },
20
20
  examples: ->(data) { examples(data.example_objects, data.cop) },
21
- configuration: ->(data) { configurations(data.cop.department, data.config, data.cop) },
21
+ configuration: ->(data) { configurations(data.cop.department, data.cop, data.config) },
22
22
  references: ->(data) { references(data.cop, data.see_objects) }
23
23
  }.freeze
24
24
 
@@ -180,17 +180,17 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
180
180
  content
181
181
  end
182
182
 
183
- def configurations(department, pars, cop)
184
- return '' if pars.empty?
185
-
183
+ def configurations(department, cop, cop_config)
186
184
  header = ['Name', 'Default value', 'Configurable values']
187
- configs = pars
185
+ configs = cop_config
188
186
  .each_key
189
187
  .reject { |key| key.start_with?('Supported') }
190
188
  .reject { |key| key.start_with?('AllowMultipleStyles') }
189
+ return '' if configs.empty?
190
+
191
191
  content = configs.map do |name|
192
- configurable = configurable_values(pars, name)
193
- default = format_table_value(pars[name])
192
+ configurable = configurable_values(cop_config, name)
193
+ default = format_table_value(cop_config[name])
194
194
 
195
195
  [configuration_name(department, name), default, configurable]
196
196
  end
@@ -206,17 +206,17 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
206
206
  end
207
207
 
208
208
  # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength
209
- def configurable_values(pars, name)
209
+ def configurable_values(cop_config, name)
210
210
  case name
211
211
  when /^Enforced/
212
212
  supported_style_name = RuboCop::Cop::Util.to_supported_styles(name)
213
- format_table_value(pars[supported_style_name])
213
+ format_table_value(cop_config[supported_style_name])
214
214
  when 'IndentationWidth'
215
215
  'Integer'
216
216
  when 'Database'
217
- format_table_value(pars['SupportedDatabases'])
217
+ format_table_value(cop_config['SupportedDatabases'])
218
218
  else
219
- case pars[name]
219
+ case cop_config[name]
220
220
  when String
221
221
  'String'
222
222
  when Integer
@@ -319,7 +319,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
319
319
  AutoCorrect Description Enabled StyleGuide Reference Safe SafeAutoCorrect VersionAdded
320
320
  VersionChanged
321
321
  ]
322
- pars = cop_config.reject { |k| non_display_keys.include? k }
322
+ parameters = cop_config.reject { |k| non_display_keys.include? k }
323
323
  description = 'No documentation'
324
324
  example_objects = safety_objects = see_objects = []
325
325
  cop_code(cop) do |code_object|
@@ -329,7 +329,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
329
329
  see_objects = code_object.tags('see')
330
330
  end
331
331
  data = CopData.new(cop: cop, description: description, example_objects: example_objects,
332
- safety_objects: safety_objects, see_objects: see_objects, config: pars)
332
+ safety_objects: safety_objects, see_objects: see_objects, config: parameters)
333
333
  cops_body(data)
334
334
  end
335
335
 
@@ -88,10 +88,15 @@ module RuboCop
88
88
  @cop_names ||= all_cops? ? all_cop_names : parsed_cop_names
89
89
  end
90
90
 
91
+ # Returns an array of cops for this directive comment, without resolving departments
92
+ def raw_cop_names
93
+ @raw_cop_names ||= (cops || '').split(/,\s*/)
94
+ end
95
+
91
96
  # Returns array of specified in this directive department names
92
97
  # when all department disabled
93
98
  def department_names
94
- splitted_cops_string.select { |cop| department?(cop) }
99
+ raw_cop_names.select { |cop| department?(cop) }
95
100
  end
96
101
 
97
102
  # Checks if directive departments include cop
@@ -101,11 +106,11 @@ module RuboCop
101
106
 
102
107
  # Checks if cop department has already used in directive comment
103
108
  def overridden_by_department?(cop)
104
- in_directive_department?(cop) && splitted_cops_string.include?(cop)
109
+ in_directive_department?(cop) && raw_cop_names.include?(cop)
105
110
  end
106
111
 
107
112
  def directive_count
108
- splitted_cops_string.count
113
+ raw_cop_names.count
109
114
  end
110
115
 
111
116
  # Returns line number for directive
@@ -115,12 +120,8 @@ module RuboCop
115
120
 
116
121
  private
117
122
 
118
- def splitted_cops_string
119
- (cops || '').split(/,\s*/)
120
- end
121
-
122
123
  def parsed_cop_names
123
- cops = splitted_cops_string.map do |name|
124
+ cops = raw_cop_names.map do |name|
124
125
  department?(name) ? cop_names_for_department(name) : name
125
126
  end.flatten
126
127
  cops - [LINT_SYNTAX_COP]
@@ -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