rubocop 1.19.1 → 1.22.1

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