rubocop 1.19.1 → 1.22.1

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 (181) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +86 -14
  4. data/lib/rubocop/config.rb +5 -0
  5. data/lib/rubocop/config_loader.rb +4 -2
  6. data/lib/rubocop/config_validator.rb +9 -1
  7. data/lib/rubocop/cop/base.rb +3 -3
  8. data/lib/rubocop/cop/bundler/gem_filename.rb +103 -0
  9. data/lib/rubocop/cop/bundler/insecure_protocol_source.rb +45 -21
  10. data/lib/rubocop/cop/bundler/ordered_gems.rb +3 -12
  11. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +2 -2
  12. data/lib/rubocop/cop/correctors/line_break_corrector.rb +1 -1
  13. data/lib/rubocop/cop/correctors/ordered_gem_corrector.rb +11 -10
  14. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +3 -12
  15. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +1 -1
  16. data/lib/rubocop/cop/generator.rb +14 -8
  17. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -1
  18. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
  19. data/lib/rubocop/cop/layout/class_structure.rb +2 -1
  20. data/lib/rubocop/cop/layout/dot_position.rb +31 -4
  21. data/lib/rubocop/cop/layout/end_alignment.rb +1 -1
  22. data/lib/rubocop/cop/layout/leading_comment_space.rb +1 -1
  23. data/lib/rubocop/cop/layout/line_length.rb +8 -6
  24. data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -1
  25. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +3 -0
  26. data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -0
  27. data/lib/rubocop/cop/layout/single_line_block_chain.rb +15 -4
  28. data/lib/rubocop/cop/layout/space_after_not.rb +1 -0
  29. data/lib/rubocop/cop/layout/space_around_equals_in_parameter_default.rb +2 -1
  30. data/lib/rubocop/cop/layout/space_around_keyword.rb +2 -2
  31. data/lib/rubocop/cop/layout/space_before_brackets.rb +1 -0
  32. data/lib/rubocop/cop/layout/space_inside_parens.rb +74 -24
  33. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +1 -1
  34. data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +111 -0
  35. data/lib/rubocop/cop/lint/ambiguous_range.rb +8 -8
  36. data/lib/rubocop/cop/lint/assignment_in_condition.rb +7 -5
  37. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +18 -5
  38. data/lib/rubocop/cop/lint/boolean_symbol.rb +5 -0
  39. data/lib/rubocop/cop/lint/debugger.rb +2 -4
  40. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +4 -4
  41. data/lib/rubocop/cop/lint/disjunctive_assignment_in_constructor.rb +24 -1
  42. data/lib/rubocop/cop/lint/else_layout.rb +9 -5
  43. data/lib/rubocop/cop/lint/empty_in_pattern.rb +1 -1
  44. data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
  45. data/lib/rubocop/cop/lint/float_out_of_range.rb +1 -1
  46. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +12 -3
  47. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +67 -0
  48. data/lib/rubocop/cop/lint/interpolation_check.rb +5 -0
  49. data/lib/rubocop/cop/lint/loop.rb +4 -3
  50. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +5 -1
  51. data/lib/rubocop/cop/lint/number_conversion.rb +12 -1
  52. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -1
  53. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +4 -2
  54. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +17 -0
  55. data/lib/rubocop/cop/lint/percent_string_array.rb +10 -0
  56. data/lib/rubocop/cop/lint/raise_exception.rb +4 -0
  57. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +5 -4
  58. data/lib/rubocop/cop/lint/require_relative_self_path.rb +50 -0
  59. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +1 -1
  60. data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
  61. data/lib/rubocop/cop/lint/triple_quotes.rb +1 -1
  62. data/lib/rubocop/cop/lint/unexpected_block_arity.rb +8 -3
  63. data/lib/rubocop/cop/lint/unused_method_argument.rb +2 -3
  64. data/lib/rubocop/cop/lint/useless_method_definition.rb +3 -2
  65. data/lib/rubocop/cop/lint/useless_setter_call.rb +7 -4
  66. data/lib/rubocop/cop/lint/useless_times.rb +4 -3
  67. data/lib/rubocop/cop/metrics/abc_size.rb +6 -0
  68. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
  69. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +1 -1
  70. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  71. data/lib/rubocop/cop/mixin/annotation_comment.rb +57 -34
  72. data/lib/rubocop/cop/mixin/code_length.rb +1 -1
  73. data/lib/rubocop/cop/mixin/documentation_comment.rb +5 -2
  74. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +23 -1
  75. data/lib/rubocop/cop/mixin/heredoc.rb +1 -3
  76. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +2 -2
  77. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +9 -1
  78. data/lib/rubocop/cop/mixin/percent_array.rb +6 -1
  79. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +9 -1
  80. data/lib/rubocop/cop/mixin/string_literals_help.rb +5 -1
  81. data/lib/rubocop/cop/naming/ascii_identifiers.rb +0 -3
  82. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  83. data/lib/rubocop/cop/naming/constant_name.rb +1 -1
  84. data/lib/rubocop/cop/naming/inclusive_language.rb +9 -9
  85. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +5 -4
  86. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +7 -0
  87. data/lib/rubocop/cop/security/io_methods.rb +49 -0
  88. data/lib/rubocop/cop/security/json_load.rb +8 -7
  89. data/lib/rubocop/cop/security/open.rb +4 -0
  90. data/lib/rubocop/cop/security/yaml_load.rb +4 -0
  91. data/lib/rubocop/cop/style/accessor_grouping.rb +2 -2
  92. data/lib/rubocop/cop/style/and_or.rb +5 -0
  93. data/lib/rubocop/cop/style/arguments_forwarding.rb +13 -2
  94. data/lib/rubocop/cop/style/array_coercion.rb +21 -3
  95. data/lib/rubocop/cop/style/ascii_comments.rb +0 -3
  96. data/lib/rubocop/cop/style/block_delimiters.rb +23 -6
  97. data/lib/rubocop/cop/style/case_equality.rb +6 -9
  98. data/lib/rubocop/cop/style/case_like_if.rb +5 -0
  99. data/lib/rubocop/cop/style/class_and_module_children.rb +9 -0
  100. data/lib/rubocop/cop/style/collection_compact.rb +7 -5
  101. data/lib/rubocop/cop/style/collection_methods.rb +8 -6
  102. data/lib/rubocop/cop/style/combinable_loops.rb +3 -2
  103. data/lib/rubocop/cop/style/comment_annotation.rb +25 -39
  104. data/lib/rubocop/cop/style/commented_keyword.rb +4 -1
  105. data/lib/rubocop/cop/style/date_time.rb +5 -0
  106. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +1 -1
  107. data/lib/rubocop/cop/style/documentation.rb +23 -8
  108. data/lib/rubocop/cop/style/double_negation.rb +15 -5
  109. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  110. data/lib/rubocop/cop/style/explicit_block_argument.rb +21 -11
  111. data/lib/rubocop/cop/style/float_division.rb +10 -2
  112. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +7 -2
  113. data/lib/rubocop/cop/style/global_std_stream.rb +4 -0
  114. data/lib/rubocop/cop/style/hash_each_methods.rb +5 -0
  115. data/lib/rubocop/cop/style/hash_except.rb +4 -3
  116. data/lib/rubocop/cop/style/hash_transform_keys.rb +4 -6
  117. data/lib/rubocop/cop/style/hash_transform_values.rb +4 -6
  118. data/lib/rubocop/cop/style/identical_conditional_branches.rb +18 -16
  119. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +18 -4
  120. data/lib/rubocop/cop/style/infinite_loop.rb +4 -3
  121. data/lib/rubocop/cop/style/inverse_methods.rb +9 -2
  122. data/lib/rubocop/cop/style/lambda_call.rb +1 -1
  123. data/lib/rubocop/cop/style/line_end_concatenation.rb +13 -0
  124. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +6 -6
  125. data/lib/rubocop/cop/style/module_function.rb +8 -9
  126. data/lib/rubocop/cop/style/mutable_constant.rb +73 -6
  127. data/lib/rubocop/cop/style/negated_if.rb +1 -1
  128. data/lib/rubocop/cop/style/negated_unless.rb +1 -1
  129. data/lib/rubocop/cop/style/non_nil_check.rb +2 -2
  130. data/lib/rubocop/cop/style/not.rb +2 -2
  131. data/lib/rubocop/cop/style/numbered_parameters.rb +46 -0
  132. data/lib/rubocop/cop/style/numbered_parameters_limit.rb +50 -0
  133. data/lib/rubocop/cop/style/numeric_literals.rb +7 -8
  134. data/lib/rubocop/cop/style/numeric_predicate.rb +5 -0
  135. data/lib/rubocop/cop/style/optional_arguments.rb +4 -0
  136. data/lib/rubocop/cop/style/optional_boolean_parameter.rb +14 -4
  137. data/lib/rubocop/cop/style/parallel_assignment.rb +1 -1
  138. data/lib/rubocop/cop/style/percent_q_literals.rb +2 -2
  139. data/lib/rubocop/cop/style/preferred_hash_methods.rb +9 -4
  140. data/lib/rubocop/cop/style/quoted_symbols.rb +10 -6
  141. data/lib/rubocop/cop/style/raise_args.rb +1 -1
  142. data/lib/rubocop/cop/style/redundant_argument.rb +19 -9
  143. data/lib/rubocop/cop/style/redundant_condition.rb +2 -3
  144. data/lib/rubocop/cop/style/redundant_fetch_block.rb +4 -0
  145. data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +12 -3
  146. data/lib/rubocop/cop/style/redundant_freeze.rb +4 -4
  147. data/lib/rubocop/cop/style/redundant_interpolation.rb +1 -1
  148. data/lib/rubocop/cop/style/redundant_percent_q.rb +2 -3
  149. data/lib/rubocop/cop/style/redundant_self.rb +10 -0
  150. data/lib/rubocop/cop/style/redundant_self_assignment.rb +4 -3
  151. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +1 -1
  152. data/lib/rubocop/cop/style/redundant_sort.rb +51 -18
  153. data/lib/rubocop/cop/style/regexp_literal.rb +3 -3
  154. data/lib/rubocop/cop/style/return_nil.rb +2 -1
  155. data/lib/rubocop/cop/style/safe_navigation.rb +13 -2
  156. data/lib/rubocop/cop/style/select_by_regexp.rb +133 -0
  157. data/lib/rubocop/cop/style/single_argument_dig.rb +5 -0
  158. data/lib/rubocop/cop/style/slicing_with_range.rb +13 -0
  159. data/lib/rubocop/cop/style/special_global_vars.rb +4 -0
  160. data/lib/rubocop/cop/style/static_class.rb +5 -5
  161. data/lib/rubocop/cop/style/string_chars.rb +4 -2
  162. data/lib/rubocop/cop/style/string_concatenation.rb +5 -1
  163. data/lib/rubocop/cop/style/string_hash_keys.rb +4 -0
  164. data/lib/rubocop/cop/style/struct_inheritance.rb +4 -0
  165. data/lib/rubocop/cop/style/swap_values.rb +4 -2
  166. data/lib/rubocop/cop/style/symbol_proc.rb +26 -0
  167. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +19 -0
  168. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  169. data/lib/rubocop/cop/style/yoda_condition.rb +24 -7
  170. data/lib/rubocop/cop/style/zero_length_predicate.rb +6 -0
  171. data/lib/rubocop/cop/util.rb +4 -3
  172. data/lib/rubocop/cops_documentation_generator.rb +17 -5
  173. data/lib/rubocop/options.rb +126 -112
  174. data/lib/rubocop/rake_task.rb +1 -1
  175. data/lib/rubocop/result_cache.rb +3 -3
  176. data/lib/rubocop/rspec/cop_helper.rb +1 -1
  177. data/lib/rubocop/rspec/expect_offense.rb +6 -2
  178. data/lib/rubocop/runner.rb +2 -3
  179. data/lib/rubocop/version.rb +1 -1
  180. data/lib/rubocop.rb +10 -2
  181. metadata +17 -9
@@ -5,9 +5,27 @@ module RuboCop
5
5
  module Style
6
6
  # This cop enforces the use of `Array()` instead of explicit `Array` check or `[*var]`.
7
7
  #
8
- # This cop is disabled by default because false positive will occur if
9
- # the argument of `Array()` is not an array (e.g. Hash, Set),
10
- # an array will be returned as an incompatibility result.
8
+ # The cop is disabled by default due to safety concerns.
9
+ #
10
+ # @safety
11
+ # This cop is unsafe because a false positive may occur if
12
+ # the argument of `Array()` is (or could be) nil or depending
13
+ # on how the argument is handled by `Array()` (which can be
14
+ # different than just wrapping the argument in an array).
15
+ #
16
+ # For example:
17
+ #
18
+ # [source,ruby]
19
+ # ----
20
+ # [nil] #=> [nil]
21
+ # Array(nil) #=> []
22
+ #
23
+ # [{a: 'b'}] #= [{a: 'b'}]
24
+ # Array({a: 'b'}) #=> [[:a, 'b']]
25
+ #
26
+ # [Time.now] #=> [#<Time ...>]
27
+ # Array(Time.now) #=> [14, 16, 14, 16, 9, 2021, 4, 259, true, "EDT"]
28
+ # ----
11
29
  #
12
30
  # @example
13
31
  # # bad
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # rubocop:disable Style/AsciiComments
4
-
5
3
  module RuboCop
6
4
  module Cop
7
5
  module Style
@@ -57,4 +55,3 @@ module RuboCop
57
55
  end
58
56
  end
59
57
  end
60
- # rubocop:enable Style/AsciiComments
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # rubocop:disable Metrics/ClassLength
3
4
  module RuboCop
4
5
  module Cop
5
6
  module Style
@@ -191,7 +192,7 @@ module RuboCop
191
192
  if node.braces?
192
193
  replace_braces_with_do_end(corrector, node.loc)
193
194
  else
194
- replace_do_end_with_braces(corrector, node.loc)
195
+ replace_do_end_with_braces(corrector, node)
195
196
  end
196
197
  end
197
198
 
@@ -256,7 +257,8 @@ module RuboCop
256
257
  corrector.replace(e, 'end')
257
258
  end
258
259
 
259
- def replace_do_end_with_braces(corrector, loc)
260
+ def replace_do_end_with_braces(corrector, node)
261
+ loc = node.loc
260
262
  b = loc.begin
261
263
  e = loc.end
262
264
 
@@ -264,6 +266,8 @@ module RuboCop
264
266
 
265
267
  corrector.replace(b, '{')
266
268
  corrector.replace(e, '}')
269
+
270
+ corrector.wrap(node.body, "begin\n", "\nend") if begin_required?(node)
267
271
  end
268
272
 
269
273
  def whitespace_before?(range)
@@ -275,14 +279,20 @@ module RuboCop
275
279
  end
276
280
 
277
281
  def move_comment_before_block(corrector, comment, block_node, closing_brace)
278
- range = range_between(closing_brace.end_pos, comment.loc.expression.end_pos)
279
-
280
- corrector.remove(range_with_surrounding_space(range: range, side: :right))
281
- corrector.insert_after(closing_brace, "\n")
282
+ range = block_node.chained? ? end_of_chain(block_node.parent).source_range : closing_brace
283
+ comment_range = range_between(range.end_pos, comment.loc.expression.end_pos)
284
+ corrector.remove(range_with_surrounding_space(range: comment_range, side: :right))
285
+ corrector.insert_after(range, "\n")
282
286
 
283
287
  corrector.insert_before(block_node, "#{comment.text}\n")
284
288
  end
285
289
 
290
+ def end_of_chain(node)
291
+ return node unless node.chained?
292
+
293
+ end_of_chain(node.parent)
294
+ end
295
+
286
296
  def get_blocks(node, &block)
287
297
  case node.type
288
298
  when :block
@@ -407,7 +417,14 @@ module RuboCop
407
417
  def array_or_range?(node)
408
418
  node.array_type? || node.range_type?
409
419
  end
420
+
421
+ def begin_required?(block_node)
422
+ # If the block contains `rescue` or `ensure`, it needs to be wrapped in
423
+ # `begin`...`end` when changing `do-end` to `{}`.
424
+ block_node.each_child_node(:rescue, :ensure).any? && !block_node.single_line?
425
+ end
410
426
  end
411
427
  end
412
428
  end
413
429
  end
430
+ # rubocop:enable Metrics/ClassLength
@@ -5,9 +5,11 @@ module RuboCop
5
5
  module Style
6
6
  # This cop checks for uses of the case equality operator(===).
7
7
  #
8
+ # If `AllowOnConstant` option is enabled, the cop will ignore violations when the receiver of
9
+ # the case equality operator is a constant.
10
+ #
8
11
  # @example
9
12
  # # bad
10
- # Array === something
11
13
  # (1..100) === 7
12
14
  # /something/ === some_string
13
15
  #
@@ -16,18 +18,13 @@ module RuboCop
16
18
  # (1..100).include?(7)
17
19
  # /something/.match?(some_string)
18
20
  #
19
- # @example AllowOnConstant
20
- # # Style/CaseEquality:
21
- # # AllowOnConstant: true
22
- #
21
+ # @example AllowOnConstant: false (default)
23
22
  # # bad
24
- # (1..100) === 7
25
- # /something/ === some_string
23
+ # Array === something
26
24
  #
25
+ # @example AllowOnConstant: true
27
26
  # # good
28
27
  # Array === something
29
- # (1..100).include?(7)
30
- # /something/.match?(some_string)
31
28
  #
32
29
  class CaseEquality < Base
33
30
  extend AutoCorrector
@@ -6,6 +6,11 @@ module RuboCop
6
6
  # This cop identifies places where `if-elsif` constructions
7
7
  # can be replaced with `case-when`.
8
8
  #
9
+ # @safety
10
+ # This cop is unsafe. `case` statements use `===` for equality,
11
+ # so if the original conditional used a different equality operator, the
12
+ # behaviour may be different.
13
+ #
9
14
  # @example
10
15
  # # bad
11
16
  # if status == :active
@@ -6,6 +6,15 @@ module RuboCop
6
6
  # This cop checks the style of children definitions at classes and
7
7
  # modules. Basically there are two different styles:
8
8
  #
9
+ # @safety
10
+ # Autocorrection is unsafe.
11
+ #
12
+ # Moving from compact to nested children requires knowledge of whether the
13
+ # outer parent is a module or a class. Moving from nested to compact requires
14
+ # verification that the outer parent is defined elsewhere. Rubocop does not
15
+ # have the knowledge to perform either operation safely and thus requires
16
+ # manual oversight.
17
+ #
9
18
  # @example EnforcedStyle: nested (default)
10
19
  # # good
11
20
  # # have each child on its own line
@@ -6,11 +6,13 @@ module RuboCop
6
6
  # This cop checks for places where custom logic on rejection nils from arrays
7
7
  # and hashes can be replaced with `{Array,Hash}#{compact,compact!}`.
8
8
  #
9
- # It is marked as unsafe by default because false positives may occur in the
10
- # nil check of block arguments to the receiver object.
11
- # For example, `[[1, 2], [3, nil]].reject { |first, second| second.nil? }`
12
- # and `[[1, 2], [3, nil]].compact` are not compatible. This will work fine
13
- # when the receiver is a hash object.
9
+ # @safety
10
+ # It is unsafe by default because false positives may occur in the
11
+ # `nil` check of block arguments to the receiver object.
12
+ #
13
+ # For example, `[[1, 2], [3, nil]].reject { |first, second| second.nil? }`
14
+ # and `[[1, 2], [3, nil]].compact` are not compatible. This will work fine
15
+ # when the receiver is a hash object.
14
16
  #
15
17
  # @example
16
18
  # # bad
@@ -6,10 +6,6 @@ module RuboCop
6
6
  # This cop enforces the use of consistent method names
7
7
  # from the Enumerable module.
8
8
  #
9
- # Unfortunately we cannot actually know if a method is from
10
- # Enumerable or not (static analysis limitation), so this cop
11
- # can yield some false positives.
12
- #
13
9
  # You can customize the mapping from undesired method to desired method.
14
10
  #
15
11
  # e.g. to use `detect` over `find`:
@@ -18,9 +14,14 @@ module RuboCop
18
14
  # PreferredMethods:
19
15
  # find: detect
20
16
  #
21
- # The default mapping for `PreferredMethods` behaves as follows.
17
+ # @safety
18
+ # This cop is unsafe because it finds methods by name, without actually
19
+ # being able to determine if the receiver is an Enumerable or not, so
20
+ # this cop may register false positives.
22
21
  #
23
22
  # @example
23
+ # # These examples are based on the default mapping for `PreferredMethods`.
24
+ #
24
25
  # # bad
25
26
  # items.collect
26
27
  # items.collect!
@@ -68,7 +69,8 @@ module RuboCop
68
69
  return false unless node.arguments.any?
69
70
 
70
71
  node.last_argument.block_pass_type? ||
71
- node.last_argument.sym_type? && methods_accepting_symbol.include?(node.method_name.to_s)
72
+ (node.last_argument.sym_type? &&
73
+ methods_accepting_symbol.include?(node.method_name.to_s))
72
74
  end
73
75
 
74
76
  def message(node)
@@ -7,8 +7,9 @@ module RuboCop
7
7
  # can be combined into a single loop. It is very likely that combining them
8
8
  # will make the code more efficient and more concise.
9
9
  #
10
- # It is marked as unsafe, because the first loop might modify
11
- # a state that the second loop depends on; these two aren't combinable.
10
+ # @safety
11
+ # The cop is unsafe, because the first loop might modify state that the
12
+ # second loop depends on; these two aren't combinable.
12
13
  #
13
14
  # @example
14
15
  # # bad
@@ -6,6 +6,9 @@ module RuboCop
6
6
  # This cop checks that comment annotation keywords are written according
7
7
  # to guidelines.
8
8
  #
9
+ # Annotation keywords can be specified by overriding the cop's `Keywords`
10
+ # configuration. Keywords are allowed to be single words or phrases.
11
+ #
9
12
  # NOTE: With a multiline comment block (where each line is only a
10
13
  # comment), only the first line will be able to register an offense, even
11
14
  # if an annotation keyword starts another line. This is done to prevent
@@ -56,7 +59,6 @@ module RuboCop
56
59
  # # good
57
60
  # # OPTIMIZE does not work
58
61
  class CommentAnnotation < Base
59
- include AnnotationComment
60
62
  include RangeHelp
61
63
  extend AutoCorrector
62
64
 
@@ -73,27 +75,27 @@ module RuboCop
73
75
  next unless first_comment_line?(processed_source.comments, index) ||
74
76
  inline_comment?(comment)
75
77
 
76
- margin, first_word, colon, space, note = split_comment(comment)
77
- next unless annotation?(comment) && !correct_annotation?(first_word, colon, space, note)
78
-
79
- range = annotation_range(comment, margin, first_word, colon, space)
78
+ annotation = AnnotationComment.new(comment, keywords)
79
+ next unless annotation.annotation? && !annotation.correct?(colon: requires_colon?)
80
80
 
81
- register_offense(range, note, first_word)
81
+ register_offense(annotation)
82
82
  end
83
83
  end
84
84
 
85
85
  private
86
86
 
87
- def register_offense(range, note, first_word)
88
- message = requires_colon? ? MSG_COLON_STYLE : MSG_SPACE_STYLE
87
+ def register_offense(annotation)
88
+ range = annotation_range(annotation)
89
+ message = if annotation.note
90
+ requires_colon? ? MSG_COLON_STYLE : MSG_SPACE_STYLE
91
+ else
92
+ MISSING_NOTE
93
+ end
89
94
 
90
- add_offense(
91
- range,
92
- message: format(note ? message : MISSING_NOTE, keyword: first_word)
93
- ) do |corrector|
94
- next if note.nil?
95
+ add_offense(range, message: format(message, keyword: annotation.keyword)) do |corrector|
96
+ next if annotation.note.nil?
95
97
 
96
- correct_offense(corrector, range, first_word)
98
+ correct_offense(corrector, range, annotation.keyword)
97
99
  end
98
100
  end
99
101
 
@@ -105,39 +107,23 @@ module RuboCop
105
107
  !comment_line?(comment.loc.expression.source_line)
106
108
  end
107
109
 
108
- def annotation_range(comment, margin, first_word, colon, space)
109
- start = comment.loc.expression.begin_pos + margin.length
110
- length = concat_length(first_word, colon, space)
111
- range_between(start, start + length)
112
- end
113
-
114
- def concat_length(*args)
115
- args.reduce(0) { |acc, elem| acc + elem.to_s.length }
110
+ def annotation_range(annotation)
111
+ range_between(*annotation.bounds)
116
112
  end
117
113
 
118
- def correct_annotation?(first_word, colon, space, note)
119
- return correct_colon_annotation?(first_word, colon, space, note) if requires_colon?
114
+ def correct_offense(corrector, range, keyword)
115
+ return corrector.replace(range, "#{keyword.upcase}: ") if requires_colon?
120
116
 
121
- correct_space_annotation?(first_word, colon, space, note)
122
- end
123
-
124
- def correct_colon_annotation?(first_word, colon, space, note)
125
- keyword?(first_word) && (colon && space && note || !colon && !note)
126
- end
127
-
128
- def correct_space_annotation?(first_word, colon, space, note)
129
- keyword?(first_word) && (!colon && space && note || !colon && !note)
130
- end
131
-
132
- def correct_offense(corrector, range, first_word)
133
- return corrector.replace(range, "#{first_word.upcase}: ") if requires_colon?
134
-
135
- corrector.replace(range, "#{first_word.upcase} ")
117
+ corrector.replace(range, "#{keyword.upcase} ")
136
118
  end
137
119
 
138
120
  def requires_colon?
139
121
  cop_config['RequireColon']
140
122
  end
123
+
124
+ def keywords
125
+ cop_config['Keywords']
126
+ end
141
127
  end
142
128
  end
143
129
  end
@@ -12,7 +12,10 @@ module RuboCop
12
12
  #
13
13
  # Auto-correction removes comments from `end` keyword and keeps comments
14
14
  # for `class`, `module`, `def` and `begin` above the keyword.
15
- # It is marked as unsafe auto-correction as it may remove meaningful comments.
15
+ #
16
+ # @safety
17
+ # Auto-correction is unsafe because it may remove a comment that is
18
+ # meaningful.
16
19
  #
17
20
  # @example
18
21
  # # bad
@@ -9,6 +9,11 @@ module RuboCop
9
9
  # replaceable in certain situations when dealing with multiple timezones
10
10
  # and/or DST.
11
11
  #
12
+ # @safety
13
+ # Autocorrection is not safe, because `DateTime` and `Time` do not have
14
+ # exactly the same behaviour, although in most cases the autocorrection
15
+ # will be fine.
16
+ #
12
17
  # @example
13
18
  #
14
19
  # # bad - uses `DateTime` for current time
@@ -86,7 +86,7 @@ module RuboCop
86
86
 
87
87
  return unless arg_node&.dstr_type? && interpolated?(arg_node)
88
88
  return if inline_comment_docs?(arg_node) ||
89
- arg_node.heredoc? && comment_block_docs?(arg_node)
89
+ (arg_node.heredoc? && comment_block_docs?(arg_node))
90
90
 
91
91
  add_offense(node.loc.selector)
92
92
  end
@@ -71,8 +71,9 @@ module RuboCop
71
71
  #
72
72
  class Documentation < Base
73
73
  include DocumentationComment
74
+ include RangeHelp
74
75
 
75
- MSG = 'Missing top-level %<type>s documentation comment.'
76
+ MSG = 'Missing top-level documentation comment for `%<type>s %<identifier>s`.'
76
77
 
77
78
  # @!method constant_definition?(node)
78
79
  def_node_matcher :constant_definition?, '{class module casgn}'
@@ -88,33 +89,35 @@ module RuboCop
88
89
  def on_class(node)
89
90
  return unless node.body
90
91
 
91
- check(node, node.body, :class)
92
+ check(node, node.body)
92
93
  end
93
94
 
94
95
  def on_module(node)
95
- check(node, node.body, :module)
96
+ check(node, node.body)
96
97
  end
97
98
 
98
99
  private
99
100
 
100
- def check(node, body, type)
101
+ def check(node, body)
101
102
  return if namespace?(body)
102
103
  return if documentation_comment?(node)
103
104
  return if constant_allowed?(node)
104
105
  return if nodoc_self_or_outer_module?(node)
105
106
  return if macro_only?(body)
106
107
 
107
- add_offense(node.loc.keyword, message: format(MSG, type: type))
108
+ range = range_between(node.loc.expression.begin_pos, node.loc.name.end_pos)
109
+ message = format(MSG, type: node.type, identifier: identifier(node))
110
+ add_offense(range, message: message)
108
111
  end
109
112
 
110
113
  def nodoc_self_or_outer_module?(node)
111
114
  nodoc_comment?(node) ||
112
- compact_namespace?(node) && nodoc_comment?(outer_module(node).first)
115
+ (compact_namespace?(node) && nodoc_comment?(outer_module(node).first))
113
116
  end
114
117
 
115
118
  def macro_only?(body)
116
- body.respond_to?(:macro?) && body.macro? ||
117
- body.respond_to?(:children) && body.children&.all? { |child| macro_only?(child) }
119
+ (body.respond_to?(:macro?) && body.macro?) ||
120
+ (body.respond_to?(:children) && body.children&.all? { |child| macro_only?(child) })
118
121
  end
119
122
 
120
123
  def namespace?(node)
@@ -165,6 +168,18 @@ module RuboCop
165
168
  def allowed_constants
166
169
  @allowed_constants ||= cop_config.fetch('AllowedConstants', []).map(&:intern)
167
170
  end
171
+
172
+ def identifier(node)
173
+ # Get the fully qualified identifier for a class/module
174
+ nodes = [node, *node.each_ancestor(:class, :module)]
175
+ nodes.reverse_each.flat_map { |n| qualify_const(n.identifier) }.join('::')
176
+ end
177
+
178
+ def qualify_const(node)
179
+ return if node.nil?
180
+
181
+ [qualify_const(node.namespace), node.short_name].compact
182
+ end
168
183
  end
169
184
  end
170
185
  end
@@ -9,6 +9,21 @@ module RuboCop
9
9
  # that use boolean as a return value. When using `EnforcedStyle: forbidden`, double negation
10
10
  # should be forbidden always.
11
11
  #
12
+ # NOTE: when `something` is a boolean value
13
+ # `!!something` and `!something.nil?` are not the same thing.
14
+ # As you're unlikely to write code that can accept values of any type
15
+ # this is rarely a problem in practice.
16
+ #
17
+ # @safety
18
+ # Autocorrection is unsafe when the value is `false`, because the result
19
+ # of the expression will change.
20
+ #
21
+ # [source,ruby]
22
+ # ----
23
+ # !!false #=> false
24
+ # !false.nil? #=> true
25
+ # ----
26
+ #
12
27
  # @example
13
28
  # # bad
14
29
  # !!something
@@ -27,11 +42,6 @@ module RuboCop
27
42
  # def foo?
28
43
  # !!return_value
29
44
  # end
30
- #
31
- # Please, note that when something is a boolean value
32
- # !!something and !something.nil? are not the same thing.
33
- # As you're unlikely to write code that can accept values of any type
34
- # this is rarely a problem in practice.
35
45
  class DoubleNegation < Base
36
46
  include ConfigurableEnforcedStyle
37
47
  extend AutoCorrector
@@ -62,7 +62,7 @@ module RuboCop
62
62
  end
63
63
 
64
64
  def correct_style?(node)
65
- compact_style? && compact?(node) || expanded_style? && expanded?(node)
65
+ (compact_style? && compact?(node)) || (expanded_style? && expanded?(node))
66
66
  end
67
67
 
68
68
  def corrected(node)
@@ -67,17 +67,27 @@ module RuboCop
67
67
  # so this can cause crashes in haml_lint
68
68
  return unless def_node
69
69
 
70
+ block_name = extract_block_name(def_node)
71
+
70
72
  add_offense(block_node) do |corrector|
71
73
  corrector.remove(block_body_range(block_node, send_node))
72
74
 
73
- add_block_argument(send_node, corrector)
74
- add_block_argument(def_node, corrector) if @def_nodes.add?(def_node)
75
+ add_block_argument(send_node, corrector, block_name)
76
+ add_block_argument(def_node, corrector, block_name) if @def_nodes.add?(def_node)
75
77
  end
76
78
  end
77
79
  end
78
80
 
79
81
  private
80
82
 
83
+ def extract_block_name(def_node)
84
+ if def_node.block_argument?
85
+ def_node.arguments.last.name
86
+ else
87
+ 'block'
88
+ end
89
+ end
90
+
81
91
  def yielding_arguments?(block_args, yield_args)
82
92
  yield_args = yield_args.dup.fill(
83
93
  nil,
@@ -91,15 +101,15 @@ module RuboCop
91
101
  end
92
102
  end
93
103
 
94
- def add_block_argument(node, corrector)
104
+ def add_block_argument(node, corrector, block_name)
95
105
  if node.arguments?
96
- insert_argument(node, corrector)
106
+ insert_argument(node, corrector, block_name)
97
107
  elsif empty_arguments?(node)
98
- corrector.replace(node.arguments, '(&block)')
108
+ corrector.replace(node.arguments, "(&#{block_name})")
99
109
  elsif call_like?(node)
100
- correct_call_node(node, corrector)
110
+ correct_call_node(node, corrector, block_name)
101
111
  else
102
- corrector.insert_after(node.loc.name, '(&block)')
112
+ corrector.insert_after(node.loc.name, "(&#{block_name})")
103
113
  end
104
114
  end
105
115
 
@@ -112,16 +122,16 @@ module RuboCop
112
122
  node.call_type? || node.zsuper_type? || node.super_type?
113
123
  end
114
124
 
115
- def insert_argument(node, corrector)
125
+ def insert_argument(node, corrector, block_name)
116
126
  last_arg = node.arguments.last
117
127
  arg_range = range_with_surrounding_comma(last_arg.source_range, :right)
118
- replacement = ' &block'
128
+ replacement = " &#{block_name}"
119
129
  replacement = ",#{replacement}" unless arg_range.source.end_with?(',')
120
130
  corrector.insert_after(arg_range, replacement) unless last_arg.blockarg_type?
121
131
  end
122
132
 
123
- def correct_call_node(node, corrector)
124
- corrector.insert_after(node, '(&block)')
133
+ def correct_call_node(node, corrector, block_name)
134
+ corrector.insert_after(node, "(&#{block_name})")
125
135
  return unless node.parenthesized?
126
136
 
127
137
  args_begin = Util.args_begin(node)
@@ -7,8 +7,16 @@ module RuboCop
7
7
  # It is recommended to either always use `fdiv` or coerce one side only.
8
8
  # This cop also provides other options for code consistency.
9
9
  #
10
- # This cop is marked as unsafe, because if operand variable is a string object
11
- # then `.to_f` will be removed and an error will occur.
10
+ # @safety
11
+ # This cop is unsafe, because if the operand variable is a string object
12
+ # then `.to_f` will be removed and an error will occur.
13
+ #
14
+ # [source,ruby]
15
+ # ----
16
+ # a = '1.2'
17
+ # b = '3.4'
18
+ # a.to_f / b.to_f # Both `to_f` calls are required here
19
+ # ----
12
20
  #
13
21
  # @example EnforcedStyle: single_coerce (default)
14
22
  # # bad
@@ -10,12 +10,17 @@ module RuboCop
10
10
  # default in future Ruby. The comment will be added below a shebang and
11
11
  # encoding comment.
12
12
  #
13
- # Note that the cop will ignore files where the comment exists but is set
13
+ # Note that the cop will accept files where the comment exists but is set
14
14
  # to `false` instead of `true`.
15
15
  #
16
16
  # To require a blank line after this comment, please see
17
17
  # `Layout/EmptyLineAfterMagicComment` cop.
18
18
  #
19
+ # @safety
20
+ # This cop's autocorrection is unsafe since any strings mutations will
21
+ # change from being accepted to raising `FrozenError`, as all strings
22
+ # will become frozen by default, and will need to be manually refactored.
23
+ #
19
24
  # @example EnforcedStyle: always (default)
20
25
  # # The `always` style will always add the frozen string literal comment
21
26
  # # to a file, regardless of the Ruby version or if `freeze` or `<<` are
@@ -141,7 +146,7 @@ module RuboCop
141
146
 
142
147
  def frozen_string_literal_comment(processed_source)
143
148
  processed_source.find_token do |token|
144
- token.text.start_with?(FrozenStringLiteral::FROZEN_STRING_LITERAL)
149
+ token.text.start_with?(FROZEN_STRING_LITERAL)
145
150
  end
146
151
  end
147
152
 
@@ -8,6 +8,10 @@ module RuboCop
8
8
  # reassign (possibly to redirect some stream) constants in Ruby, you'll get
9
9
  # an interpreter warning if you do so.
10
10
  #
11
+ # @safety
12
+ # Autocorrection is unsafe because `STDOUT` and `$stdout` may point to different
13
+ # objects, for example.
14
+ #
11
15
  # @example
12
16
  # # bad
13
17
  # STDOUT.puts('hello')
@@ -9,6 +9,11 @@ module RuboCop
9
9
  # parentheses around the block arguments to indicate that you're not
10
10
  # working with a hash, and suppress RuboCop offenses.
11
11
  #
12
+ # @safety
13
+ # This cop is unsafe because it cannot be guaranteed that the receiver
14
+ # is a `Hash`. The `AllowedReceivers` configuration can mitigate,
15
+ # but not fully resolve, this safety issue.
16
+ #
12
17
  # @example
13
18
  # # bad
14
19
  # hash.keys.each { |k| p k }