rubocop 1.19.0 → 1.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) 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/documentation.rb +1 -1
  15. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +3 -12
  16. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +1 -1
  17. data/lib/rubocop/cop/generator.rb +14 -8
  18. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -1
  19. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
  20. data/lib/rubocop/cop/layout/class_structure.rb +2 -1
  21. data/lib/rubocop/cop/layout/dot_position.rb +25 -2
  22. data/lib/rubocop/cop/layout/end_alignment.rb +1 -1
  23. data/lib/rubocop/cop/layout/leading_comment_space.rb +1 -1
  24. data/lib/rubocop/cop/layout/line_length.rb +8 -6
  25. data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -1
  26. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +3 -0
  27. data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -0
  28. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +5 -4
  29. data/lib/rubocop/cop/layout/single_line_block_chain.rb +15 -4
  30. data/lib/rubocop/cop/layout/space_after_not.rb +1 -0
  31. data/lib/rubocop/cop/layout/space_around_equals_in_parameter_default.rb +2 -1
  32. data/lib/rubocop/cop/layout/space_around_keyword.rb +2 -2
  33. data/lib/rubocop/cop/layout/space_before_brackets.rb +1 -0
  34. data/lib/rubocop/cop/layout/space_inside_parens.rb +74 -24
  35. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +1 -1
  36. data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +111 -0
  37. data/lib/rubocop/cop/lint/ambiguous_range.rb +8 -8
  38. data/lib/rubocop/cop/lint/assignment_in_condition.rb +7 -5
  39. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +18 -5
  40. data/lib/rubocop/cop/lint/boolean_symbol.rb +5 -0
  41. data/lib/rubocop/cop/lint/debugger.rb +2 -4
  42. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +4 -4
  43. data/lib/rubocop/cop/lint/disjunctive_assignment_in_constructor.rb +24 -1
  44. data/lib/rubocop/cop/lint/else_layout.rb +9 -5
  45. data/lib/rubocop/cop/lint/empty_in_pattern.rb +1 -1
  46. data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
  47. data/lib/rubocop/cop/lint/float_out_of_range.rb +1 -1
  48. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +12 -3
  49. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +67 -0
  50. data/lib/rubocop/cop/lint/interpolation_check.rb +5 -0
  51. data/lib/rubocop/cop/lint/loop.rb +4 -3
  52. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +5 -1
  53. data/lib/rubocop/cop/lint/number_conversion.rb +12 -1
  54. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -1
  55. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +4 -2
  56. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +17 -0
  57. data/lib/rubocop/cop/lint/percent_string_array.rb +10 -0
  58. data/lib/rubocop/cop/lint/raise_exception.rb +4 -0
  59. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +5 -4
  60. data/lib/rubocop/cop/lint/require_relative_self_path.rb +49 -0
  61. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +1 -1
  62. data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
  63. data/lib/rubocop/cop/lint/triple_quotes.rb +1 -1
  64. data/lib/rubocop/cop/lint/unexpected_block_arity.rb +8 -3
  65. data/lib/rubocop/cop/lint/unused_method_argument.rb +2 -3
  66. data/lib/rubocop/cop/lint/useless_method_definition.rb +3 -2
  67. data/lib/rubocop/cop/lint/useless_setter_call.rb +7 -4
  68. data/lib/rubocop/cop/lint/useless_times.rb +4 -3
  69. data/lib/rubocop/cop/metrics/abc_size.rb +6 -0
  70. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
  71. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +1 -1
  72. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  73. data/lib/rubocop/cop/mixin/annotation_comment.rb +57 -34
  74. data/lib/rubocop/cop/mixin/code_length.rb +1 -1
  75. data/lib/rubocop/cop/mixin/documentation_comment.rb +5 -2
  76. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +23 -1
  77. data/lib/rubocop/cop/mixin/heredoc.rb +1 -3
  78. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +2 -2
  79. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +9 -1
  80. data/lib/rubocop/cop/mixin/percent_array.rb +11 -3
  81. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +9 -1
  82. data/lib/rubocop/cop/naming/ascii_identifiers.rb +0 -3
  83. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  84. data/lib/rubocop/cop/naming/constant_name.rb +1 -1
  85. data/lib/rubocop/cop/naming/inclusive_language.rb +9 -9
  86. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +5 -4
  87. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +7 -0
  88. data/lib/rubocop/cop/security/io_methods.rb +49 -0
  89. data/lib/rubocop/cop/security/json_load.rb +8 -7
  90. data/lib/rubocop/cop/security/open.rb +4 -0
  91. data/lib/rubocop/cop/security/yaml_load.rb +4 -0
  92. data/lib/rubocop/cop/style/accessor_grouping.rb +2 -2
  93. data/lib/rubocop/cop/style/and_or.rb +5 -0
  94. data/lib/rubocop/cop/style/arguments_forwarding.rb +13 -2
  95. data/lib/rubocop/cop/style/array_coercion.rb +21 -3
  96. data/lib/rubocop/cop/style/ascii_comments.rb +0 -3
  97. data/lib/rubocop/cop/style/block_delimiters.rb +23 -6
  98. data/lib/rubocop/cop/style/case_equality.rb +6 -9
  99. data/lib/rubocop/cop/style/case_like_if.rb +5 -0
  100. data/lib/rubocop/cop/style/class_and_module_children.rb +9 -0
  101. data/lib/rubocop/cop/style/collection_compact.rb +7 -5
  102. data/lib/rubocop/cop/style/collection_methods.rb +8 -6
  103. data/lib/rubocop/cop/style/combinable_loops.rb +3 -2
  104. data/lib/rubocop/cop/style/comment_annotation.rb +25 -39
  105. data/lib/rubocop/cop/style/commented_keyword.rb +4 -1
  106. data/lib/rubocop/cop/style/date_time.rb +5 -0
  107. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +1 -1
  108. data/lib/rubocop/cop/style/documentation.rb +23 -8
  109. data/lib/rubocop/cop/style/double_negation.rb +27 -6
  110. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  111. data/lib/rubocop/cop/style/encoding.rb +26 -15
  112. data/lib/rubocop/cop/style/explicit_block_argument.rb +21 -11
  113. data/lib/rubocop/cop/style/float_division.rb +10 -2
  114. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +7 -2
  115. data/lib/rubocop/cop/style/global_std_stream.rb +4 -0
  116. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +11 -0
  117. data/lib/rubocop/cop/style/hash_each_methods.rb +5 -0
  118. data/lib/rubocop/cop/style/hash_except.rb +4 -3
  119. data/lib/rubocop/cop/style/hash_transform_keys.rb +4 -6
  120. data/lib/rubocop/cop/style/hash_transform_values.rb +4 -6
  121. data/lib/rubocop/cop/style/identical_conditional_branches.rb +18 -16
  122. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +18 -4
  123. data/lib/rubocop/cop/style/infinite_loop.rb +4 -3
  124. data/lib/rubocop/cop/style/inverse_methods.rb +9 -2
  125. data/lib/rubocop/cop/style/lambda_call.rb +1 -1
  126. data/lib/rubocop/cop/style/line_end_concatenation.rb +13 -0
  127. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +6 -6
  128. data/lib/rubocop/cop/style/module_function.rb +8 -9
  129. data/lib/rubocop/cop/style/mutable_constant.rb +73 -6
  130. data/lib/rubocop/cop/style/negated_if.rb +1 -1
  131. data/lib/rubocop/cop/style/negated_unless.rb +1 -1
  132. data/lib/rubocop/cop/style/non_nil_check.rb +2 -2
  133. data/lib/rubocop/cop/style/not.rb +2 -2
  134. data/lib/rubocop/cop/style/numbered_parameters.rb +46 -0
  135. data/lib/rubocop/cop/style/numbered_parameters_limit.rb +50 -0
  136. data/lib/rubocop/cop/style/numeric_literals.rb +7 -8
  137. data/lib/rubocop/cop/style/numeric_predicate.rb +5 -0
  138. data/lib/rubocop/cop/style/optional_arguments.rb +4 -0
  139. data/lib/rubocop/cop/style/optional_boolean_parameter.rb +14 -4
  140. data/lib/rubocop/cop/style/parallel_assignment.rb +1 -1
  141. data/lib/rubocop/cop/style/percent_q_literals.rb +2 -2
  142. data/lib/rubocop/cop/style/preferred_hash_methods.rb +9 -4
  143. data/lib/rubocop/cop/style/raise_args.rb +1 -1
  144. data/lib/rubocop/cop/style/redundant_argument.rb +14 -7
  145. data/lib/rubocop/cop/style/redundant_begin.rb +25 -0
  146. data/lib/rubocop/cop/style/redundant_condition.rb +2 -3
  147. data/lib/rubocop/cop/style/redundant_fetch_block.rb +4 -0
  148. data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +12 -3
  149. data/lib/rubocop/cop/style/redundant_freeze.rb +4 -4
  150. data/lib/rubocop/cop/style/redundant_interpolation.rb +1 -1
  151. data/lib/rubocop/cop/style/redundant_percent_q.rb +2 -3
  152. data/lib/rubocop/cop/style/redundant_self.rb +10 -0
  153. data/lib/rubocop/cop/style/redundant_self_assignment.rb +4 -3
  154. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +23 -28
  155. data/lib/rubocop/cop/style/redundant_sort.rb +51 -18
  156. data/lib/rubocop/cop/style/regexp_literal.rb +3 -3
  157. data/lib/rubocop/cop/style/return_nil.rb +2 -1
  158. data/lib/rubocop/cop/style/safe_navigation.rb +13 -2
  159. data/lib/rubocop/cop/style/select_by_regexp.rb +106 -0
  160. data/lib/rubocop/cop/style/single_argument_dig.rb +5 -0
  161. data/lib/rubocop/cop/style/slicing_with_range.rb +13 -0
  162. data/lib/rubocop/cop/style/sole_nested_conditional.rb +4 -0
  163. data/lib/rubocop/cop/style/special_global_vars.rb +4 -0
  164. data/lib/rubocop/cop/style/static_class.rb +5 -5
  165. data/lib/rubocop/cop/style/string_chars.rb +4 -2
  166. data/lib/rubocop/cop/style/string_concatenation.rb +5 -1
  167. data/lib/rubocop/cop/style/string_hash_keys.rb +4 -0
  168. data/lib/rubocop/cop/style/struct_inheritance.rb +4 -0
  169. data/lib/rubocop/cop/style/swap_values.rb +4 -2
  170. data/lib/rubocop/cop/style/symbol_array.rb +3 -3
  171. data/lib/rubocop/cop/style/symbol_proc.rb +26 -0
  172. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +19 -0
  173. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  174. data/lib/rubocop/cop/style/word_array.rb +3 -3
  175. data/lib/rubocop/cop/style/yoda_condition.rb +24 -7
  176. data/lib/rubocop/cop/style/zero_length_predicate.rb +6 -0
  177. data/lib/rubocop/cop/util.rb +2 -2
  178. data/lib/rubocop/cops_documentation_generator.rb +17 -5
  179. data/lib/rubocop/magic_comment.rb +44 -15
  180. data/lib/rubocop/options.rb +126 -112
  181. data/lib/rubocop/result_cache.rb +1 -1
  182. data/lib/rubocop/rspec/cop_helper.rb +1 -1
  183. data/lib/rubocop/rspec/expect_offense.rb +6 -2
  184. data/lib/rubocop/runner.rb +1 -2
  185. data/lib/rubocop/version.rb +1 -1
  186. data/lib/rubocop.rb +10 -2
  187. metadata +13 -5
@@ -107,7 +107,7 @@ module RuboCop
107
107
  private
108
108
 
109
109
  def allowed_slash_literal?(node)
110
- style == :slashes && !contains_disallowed_slash?(node) || allowed_mixed_slash?(node)
110
+ (style == :slashes && !contains_disallowed_slash?(node)) || allowed_mixed_slash?(node)
111
111
  end
112
112
 
113
113
  def allowed_mixed_slash?(node)
@@ -115,13 +115,13 @@ module RuboCop
115
115
  end
116
116
 
117
117
  def allowed_percent_r_literal?(node)
118
- style == :slashes && contains_disallowed_slash?(node) ||
118
+ (style == :slashes && contains_disallowed_slash?(node)) ||
119
119
  style == :percent_r ||
120
120
  allowed_mixed_percent_r?(node) || allowed_omit_parentheses_with_percent_r_literal?(node)
121
121
  end
122
122
 
123
123
  def allowed_mixed_percent_r?(node)
124
- style == :mixed && node.multiline? || contains_disallowed_slash?(node)
124
+ (style == :mixed && node.multiline?) || contains_disallowed_slash?(node)
125
125
  end
126
126
 
127
127
  def contains_disallowed_slash?(node)
@@ -74,7 +74,8 @@ module RuboCop
74
74
  end
75
75
 
76
76
  def correct_style?(node)
77
- style == :return && !return_nil_node?(node) || style == :return_nil && !return_node?(node)
77
+ (style == :return && !return_nil_node?(node)) ||
78
+ (style == :return_nil && !return_node?(node))
78
79
  end
79
80
 
80
81
  def scoped_node?(node)
@@ -10,14 +10,25 @@ module RuboCop
10
10
  # need to be changed to use safe navigation. We have limited the cop to
11
11
  # not register an offense for method chains that exceed 2 methods.
12
12
  #
13
- # Configuration option: ConvertCodeThatCanStartToReturnNil
14
- # The default for this is `false`. When configured to `true`, this will
13
+ # The default for `ConvertCodeThatCanStartToReturnNil` is `false`.
14
+ # When configured to `true`, this will
15
15
  # check for code in the format `!foo.nil? && foo.bar`. As it is written,
16
16
  # the return of this code is limited to `false` and whatever the return
17
17
  # of the method is. If this is converted to safe navigation,
18
18
  # `foo&.bar` can start returning `nil` as well as what the method
19
19
  # returns.
20
20
  #
21
+ # @safety
22
+ # Autocorrection is unsafe because if a value is `false`, the resulting
23
+ # code will have different behaviour or raise an error.
24
+ #
25
+ # [source,ruby]
26
+ # ----
27
+ # x = false
28
+ # x && x.foo # return false
29
+ # x&.foo # raises NoMethodError
30
+ # ----
31
+ #
21
32
  # @example
22
33
  # # bad
23
34
  # foo.bar if foo
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop looks for places where an subset of an array
7
+ # is calculated based on a `Regexp` match, and suggests `grep` or
8
+ # `grep_v` instead.
9
+ #
10
+ # NOTE: `grep` and `grep_v` were optimized when used without a block
11
+ # in Ruby 3.0, but may be slower in previous versions.
12
+ # See https://bugs.ruby-lang.org/issues/17030
13
+ #
14
+ # @safety
15
+ # Autocorrection is marked as unsafe because `MatchData` will
16
+ # not be created by `grep`, but may have previously been relied
17
+ # upon after the `match?` or `=~` call.
18
+ #
19
+ # @example
20
+ # # bad (select or find_all)
21
+ # array.select { |x| x.match? /regexp/ }
22
+ # array.select { |x| /regexp/.match?(x) }
23
+ # array.select { |x| x =~ /regexp/ }
24
+ # array.select { |x| /regexp/ =~ x }
25
+ #
26
+ # # bad (reject)
27
+ # array.reject { |x| x.match? /regexp/ }
28
+ # array.reject { |x| /regexp/.match?(x) }
29
+ # array.reject { |x| x =~ /regexp/ }
30
+ # array.reject { |x| /regexp/ =~ x }
31
+ #
32
+ # # good
33
+ # array.grep(regexp)
34
+ # array.grep_v(regexp)
35
+ class SelectByRegexp < Base
36
+ extend AutoCorrector
37
+ include RangeHelp
38
+
39
+ MSG = 'Prefer `%<replacement>s` to `%<original_method>s` with a regexp match.'
40
+ RESTRICT_ON_SEND = %i[select find_all reject].freeze
41
+ REPLACEMENTS = { select: 'grep', find_all: 'grep', reject: 'grep_v' }.freeze
42
+ REGEXP_METHODS = %i[match? =~].to_set.freeze
43
+
44
+ # @!method regexp_match?(node)
45
+ def_node_matcher :regexp_match?, <<~PATTERN
46
+ {
47
+ (block send (args (arg $_)) ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
48
+ (numblock send $1 ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
49
+ }
50
+ PATTERN
51
+
52
+ # @!method calls_lvar?(node, name)
53
+ def_node_matcher :calls_lvar?, <<~PATTERN
54
+ {
55
+ (send (lvar %1) ...)
56
+ (send ... (lvar %1))
57
+ (match-with-lvasgn regexp (lvar %1))
58
+ }
59
+ PATTERN
60
+
61
+ def on_send(node)
62
+ return unless (block_node = node.block_node)
63
+ return if block_node.body.begin_type?
64
+ return unless (regexp_method_send_node = extract_send_node(block_node))
65
+
66
+ regexp = find_regexp(regexp_method_send_node)
67
+ register_offense(node, block_node, regexp)
68
+ end
69
+
70
+ private
71
+
72
+ def register_offense(node, block_node, regexp)
73
+ replacement = REPLACEMENTS[node.method_name.to_sym]
74
+ message = format(MSG, replacement: replacement, original_method: node.method_name)
75
+
76
+ add_offense(block_node, message: message) do |corrector|
77
+ # Only correct if it can be determined what the regexp is
78
+ if regexp
79
+ range = range_between(node.loc.selector.begin_pos, block_node.loc.end.end_pos)
80
+ corrector.replace(range, "#{replacement}(#{regexp.source})")
81
+ end
82
+ end
83
+ end
84
+
85
+ def extract_send_node(block_node)
86
+ return unless (block_arg_name, regexp_method_send_node = regexp_match?(block_node))
87
+
88
+ block_arg_name = :"_#{block_arg_name}" if block_node.numblock_type?
89
+ return unless calls_lvar?(regexp_method_send_node, block_arg_name)
90
+
91
+ regexp_method_send_node
92
+ end
93
+
94
+ def find_regexp(node)
95
+ return node.child_nodes.first if node.match_with_lvasgn_type?
96
+
97
+ if node.receiver.lvar_type?
98
+ node.first_argument
99
+ elsif node.first_argument.lvar_type?
100
+ node.receiver
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -6,6 +6,11 @@ module RuboCop
6
6
  # Sometimes using dig method ends up with just a single
7
7
  # argument. In such cases, dig should be replaced with [].
8
8
  #
9
+ # @safety
10
+ # This cop is unsafe because it cannot be guaranteed that the receiver
11
+ # is an `Enumerable` or does not have a nonstandard implementation
12
+ # of `dig`.
13
+ #
9
14
  # @example
10
15
  # # bad
11
16
  # { key: 'value' }.dig(:key)
@@ -6,6 +6,19 @@ module RuboCop
6
6
  # This cop checks that arrays are sliced with endless ranges instead of
7
7
  # `ary[start..-1]` on Ruby 2.6+.
8
8
  #
9
+ # @safety
10
+ # This cop is unsafe because `x..-1` and `x..` are only guaranteed to
11
+ # be equivalent for `Array#[]`, and the cop cannot determine what class
12
+ # the receiver is.
13
+ #
14
+ # For example:
15
+ # [source,ruby]
16
+ # ----
17
+ # sum = proc { |ary| ary.sum }
18
+ # sum[-3..-1] # => -6
19
+ # sum[-3..] # Hangs forever
20
+ # ----
21
+ #
9
22
  # @example
10
23
  # # bad
11
24
  # items[1..-1]
@@ -38,6 +38,10 @@ module RuboCop
38
38
 
39
39
  MSG = 'Consider merging nested conditions into outer `%<conditional_type>s` conditions.'
40
40
 
41
+ def self.autocorrect_incompatible_with
42
+ [Style::NegatedIf, Style::NegatedUnless]
43
+ end
44
+
41
45
  def on_if(node)
42
46
  return if node.ternary? || node.else? || node.elsif?
43
47
 
@@ -9,6 +9,10 @@ module RuboCop
9
9
  # will add a require statement to the top of the file if
10
10
  # enabled by RequireEnglish config.
11
11
  #
12
+ # @safety
13
+ # Autocorrection is marked as unsafe because if `RequireEnglish` is not
14
+ # true, replacing perl-style variables with english variables will break.
15
+ #
12
16
  # @example EnforcedStyle: use_english_names (default)
13
17
  # # good
14
18
  # require 'English' # or this could be in another file.
@@ -7,9 +7,10 @@ module RuboCop
7
7
  # replaced with a module. Classes should be used only when it makes sense to create
8
8
  # instances out of them.
9
9
  #
10
- # This cop is marked as unsafe, because it is possible that this class is a parent
11
- # for some other subclass, monkey-patched with instance methods or
12
- # a dummy instance is instantiated from it somewhere.
10
+ # @safety
11
+ # This cop is unsafe, because it is possible that this class is a parent
12
+ # for some other subclass, monkey-patched with instance methods or
13
+ # a dummy instance is instantiated from it somewhere.
13
14
  #
14
15
  # @example
15
16
  # # bad
@@ -60,8 +61,7 @@ module RuboCop
60
61
  return false if nodes.empty?
61
62
 
62
63
  nodes.all? do |node|
63
- node_visibility(node) == :public &&
64
- node.defs_type? ||
64
+ (node_visibility(node) == :public && node.defs_type?) ||
65
65
  sclass_convertible_to_module?(node) ||
66
66
  node.equals_asgn? ||
67
67
  extend_call?(node)
@@ -5,8 +5,10 @@ module RuboCop
5
5
  module Style
6
6
  # Checks for uses of `String#split` with empty string or regexp literal argument.
7
7
  #
8
- # This cop is marked as unsafe. But probably it's quite unlikely that some other class would
9
- # define a `split` method that takes exactly the same arguments.
8
+ # @safety
9
+ # This cop is unsafe because it cannot be guaranteed that the receiver
10
+ # is actually a string. If another class has a `split` method with
11
+ # different behaviour, it would be registered as a false positive.
10
12
  #
11
13
  # @example
12
14
  # # bad
@@ -23,6 +23,10 @@ module RuboCop
23
23
  # This is useful when the receiver is some expression that returns string like `Pathname`
24
24
  # instead of a string literal.
25
25
  #
26
+ # @safety
27
+ # This cop is unsafe in `aggressive` mode, as it cannot be guaranteed that
28
+ # the receiver is actually a string, which can result in a false positive.
29
+ #
26
30
  # @example Mode: aggressive (default)
27
31
  # # bad
28
32
  # email_with_name = user.name + ' <' + user.email + '>'
@@ -93,7 +97,7 @@ module RuboCop
93
97
 
94
98
  def offensive_for_mode?(receiver_node)
95
99
  mode = cop_config['Mode'].to_sym
96
- mode == :aggressive || mode == :conservative && receiver_node.str_type?
100
+ mode == :aggressive || (mode == :conservative && receiver_node.str_type?)
97
101
  end
98
102
 
99
103
  def line_end_concatenation?(node)
@@ -6,6 +6,10 @@ module RuboCop
6
6
  # This cop checks for the use of strings as keys in hashes. The use of
7
7
  # symbols is preferred instead.
8
8
  #
9
+ # @safety
10
+ # This cop is unsafe because while symbols are preferred for hash keys,
11
+ # there are instances when string keys are required.
12
+ #
9
13
  # @example
10
14
  # # bad
11
15
  # { 'one' => 1, 'two' => 2, 'three' => 3 }
@@ -5,6 +5,10 @@ module RuboCop
5
5
  module Style
6
6
  # This cop checks for inheritance from Struct.new.
7
7
  #
8
+ # @safety
9
+ # Auto-correction is unsafe because it will change the inheritance
10
+ # tree (e.g. return value of `Module#ancestors`) of the constant.
11
+ #
8
12
  # @example
9
13
  # # bad
10
14
  # class Person < Struct.new(:first_name, :last_name)
@@ -4,8 +4,10 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # This cop enforces the use of shorthand-style swapping of 2 variables.
7
- # Its autocorrection is marked as unsafe, because it can erroneously remove
8
- # the temporary variable which is used later.
7
+ #
8
+ # @safety
9
+ # Autocorrection is unsafe, because the temporary variable used to
10
+ # swap variables will be removed, but may be referred to elsewhere.
9
11
  #
10
12
  # @example
11
13
  # # bad
@@ -35,7 +35,7 @@ module RuboCop
35
35
  extend AutoCorrector
36
36
 
37
37
  PERCENT_MSG = 'Use `%i` or `%I` for an array of symbols.'
38
- ARRAY_MSG = 'Use `[]` for an array of symbols.'
38
+ ARRAY_MSG = 'Use `%<prefer>s` for an array of symbols.'
39
39
 
40
40
  class << self
41
41
  attr_accessor :largest_brackets
@@ -60,7 +60,7 @@ module RuboCop
60
60
  end
61
61
  end
62
62
 
63
- def correct_bracketed(corrector, node)
63
+ def build_bracketed_array(node)
64
64
  syms = node.children.map do |c|
65
65
  if c.dsym_type?
66
66
  string_literal = to_string_literal(c.source)
@@ -71,7 +71,7 @@ module RuboCop
71
71
  end
72
72
  end
73
73
 
74
- corrector.replace(node, "[#{syms.join(', ')}]")
74
+ "[#{syms.join(', ')}]"
75
75
  end
76
76
 
77
77
  def to_symbol_literal(string)
@@ -8,6 +8,32 @@ module RuboCop
8
8
  # If you prefer a style that allows block for method with arguments,
9
9
  # please set `true` to `AllowMethodsWithArguments`.
10
10
  #
11
+ # @safety
12
+ # This cop is unsafe because `proc`s and blocks work differently
13
+ # when additional arguments are passed in. A block will silently
14
+ # ignore additional arguments, but a `proc` will raise
15
+ # an `ArgumentError`.
16
+ #
17
+ # For example:
18
+ #
19
+ # [source,ruby]
20
+ # ----
21
+ # class Foo
22
+ # def bar
23
+ # :bar
24
+ # end
25
+ # end
26
+ #
27
+ # def call(options = {}, &block)
28
+ # block.call(Foo.new, options)
29
+ # end
30
+ #
31
+ # call { |x| x.bar }
32
+ # #=> :bar
33
+ # call(&:bar)
34
+ # # ArgumentError: wrong number of arguments (given 1, expected 0)
35
+ # ----
36
+ #
11
37
  # @example
12
38
  # # bad
13
39
  # something.map { |s| s.upcase }
@@ -8,6 +8,25 @@ module RuboCop
8
8
  # that comma to be present. Blocks with more than one argument never
9
9
  # require a trailing comma.
10
10
  #
11
+ # @safety
12
+ # This cop is unsafe because a trailing comma can indicate there are
13
+ # more parameters that are not used.
14
+ #
15
+ # For example:
16
+ # [source,ruby]
17
+ # ----
18
+ # # with a trailing comma
19
+ # {foo: 1, bar: 2, baz: 3}.map {|key,| key }
20
+ # #=> [:foo, :bar, :baz]
21
+ #
22
+ # # without a trailing comma
23
+ # {foo: 1, bar: 2, baz: 3}.map {|key| key }
24
+ # #=> [[:foo, 1], [:bar, 2], [:baz, 3]]
25
+ # ----
26
+ #
27
+ # This can be fixed by replacing the trailing comma with a placeholder
28
+ # argument (such as `|key, _value|`).
29
+ #
11
30
  # @example
12
31
  # # bad
13
32
  # add { |foo, bar,| foo + bar }
@@ -192,7 +192,7 @@ module RuboCop
192
192
 
193
193
  def allowed_method_name?(node)
194
194
  allowed_method_names.include?(node.method_name) ||
195
- exact_name_match? && !names_match?(node)
195
+ (exact_name_match? && !names_match?(node))
196
196
  end
197
197
 
198
198
  def allowed_writer?(method_name)
@@ -44,7 +44,7 @@ module RuboCop
44
44
  extend AutoCorrector
45
45
 
46
46
  PERCENT_MSG = 'Use `%w` or `%W` for an array of words.'
47
- ARRAY_MSG = 'Use `[]` for an array of words.'
47
+ ARRAY_MSG = 'Use `%<prefer>s` for an array of words.'
48
48
 
49
49
  class << self
50
50
  attr_accessor :largest_brackets
@@ -82,7 +82,7 @@ module RuboCop
82
82
  Regexp.new(cop_config['WordRegex'])
83
83
  end
84
84
 
85
- def correct_bracketed(corrector, node)
85
+ def build_bracketed_array(node)
86
86
  words = node.children.map do |word|
87
87
  if word.dstr_type?
88
88
  string_literal = to_string_literal(word.source)
@@ -93,7 +93,7 @@ module RuboCop
93
93
  end
94
94
  end
95
95
 
96
- corrector.replace(node, "[#{words.join(', ')}]")
96
+ "[#{words.join(', ')}]"
97
97
  end
98
98
  end
99
99
  end
@@ -7,6 +7,26 @@ module RuboCop
7
7
  # i.e. comparison operations where the order of expression is reversed.
8
8
  # eg. `5 == x`
9
9
  #
10
+ # @safety
11
+ # This cop is unsafe because comparison operators can be defined
12
+ # differently on different classes, and are not guaranteed to
13
+ # have the same result if reversed.
14
+ #
15
+ # For example:
16
+ #
17
+ # [source,ruby]
18
+ # ----
19
+ # class MyKlass
20
+ # def ==(other)
21
+ # true
22
+ # end
23
+ # end
24
+ #
25
+ # obj = MyKlass.new
26
+ # obj == 'string' #=> true
27
+ # 'string' == obj #=> false
28
+ # ----
29
+ #
10
30
  # @example EnforcedStyle: forbid_for_all_comparison_operators (default)
11
31
  # # bad
12
32
  # 99 == foo
@@ -58,14 +78,11 @@ module RuboCop
58
78
  extend AutoCorrector
59
79
 
60
80
  MSG = 'Reverse the order of the operands `%<source>s`.'
61
-
62
81
  REVERSE_COMPARISON = { '<' => '>', '<=' => '>=', '>' => '<', '>=' => '<=' }.freeze
63
-
64
82
  EQUALITY_OPERATORS = %i[== !=].freeze
65
-
66
83
  NONCOMMUTATIVE_OPERATORS = %i[===].freeze
67
-
68
84
  PROGRAM_NAMES = %i[$0 $PROGRAM_NAME].freeze
85
+ RESTRICT_ON_SEND = RuboCop::AST::Node::COMPARISON_OPERATORS
69
86
 
70
87
  # @!method file_constant_equal_program_name?(node)
71
88
  def_node_matcher :file_constant_equal_program_name?, <<~PATTERN
@@ -74,7 +91,7 @@ module RuboCop
74
91
 
75
92
  def on_send(node)
76
93
  return unless yoda_compatible_condition?(node)
77
- return if equality_only? && non_equality_operator?(node) ||
94
+ return if (equality_only? && non_equality_operator?(node)) ||
78
95
  file_constant_equal_program_name?(node)
79
96
 
80
97
  valid_yoda?(node) || add_offense(node) do |corrector|
@@ -102,8 +119,8 @@ module RuboCop
102
119
  lhs = node.receiver
103
120
  rhs = node.first_argument
104
121
 
105
- return true if lhs.literal? && rhs.literal? ||
106
- !lhs.literal? && !rhs.literal? ||
122
+ return true if (lhs.literal? && rhs.literal?) ||
123
+ (!lhs.literal? && !rhs.literal?) ||
107
124
  interpolation?(lhs)
108
125
 
109
126
  enforce_yoda? ? lhs.literal? : rhs.literal?
@@ -9,6 +9,12 @@ module RuboCop
9
9
  # receiver.length < 1 and receiver.size == 0 that can be
10
10
  # replaced by receiver.empty? and !receiver.empty?.
11
11
  #
12
+ # @safety
13
+ # This cop is unsafe because it cannot be guaranteed that the receiver
14
+ # has an `empty?` method that is defined in terms of `length`. If there
15
+ # is a non-standard class that redefines `length` or `empty?`, the cop
16
+ # may register a false positive.
17
+ #
12
18
  # @example
13
19
  # # bad
14
20
  # [1, 2, 3].length == 0
@@ -129,8 +129,8 @@ module RuboCop
129
129
  node1.respond_to?(:loc) && node2.respond_to?(:loc) && node1.loc.line == node2.loc.line
130
130
  end
131
131
 
132
- def indent(node)
133
- ' ' * node.loc.column
132
+ def indent(node, offset: 0)
133
+ ' ' * (node.loc.column + offset)
134
134
  end
135
135
 
136
136
  def to_supported_styles(enforced_style)
@@ -33,11 +33,12 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
33
33
  cops.with_department(department).sort!
34
34
  end
35
35
 
36
- def cops_body(cop, description, examples_objects, pars)
36
+ def cops_body(cop, description, examples_objects, safety_objects, pars) # rubocop:disable Metrics/AbcSize
37
37
  content = h2(cop.cop_name)
38
38
  content << required_ruby_version(cop)
39
39
  content << properties(cop)
40
40
  content << "#{description}\n"
41
+ content << safety_object(safety_objects) if safety_objects.any? { |s| !s.text.blank? }
41
42
  content << examples(examples_objects) if examples_objects.count.positive?
42
43
  content << configurations(pars)
43
44
  content << references(cop)
@@ -52,6 +53,16 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
52
53
  end
53
54
  end
54
55
 
56
+ def safety_object(safety_object_objects)
57
+ safety_object_objects.each_with_object(h3('Safety').dup) do |safety_object, content|
58
+ next if safety_object.text.blank?
59
+
60
+ content << "\n" unless content.end_with?("\n\n")
61
+ content << safety_object.text
62
+ content << "\n"
63
+ end
64
+ end
65
+
55
66
  def required_ruby_version(cop)
56
67
  return '' unless cop.respond_to?(:required_minimum_ruby_version)
57
68
 
@@ -61,8 +72,8 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
61
72
  # rubocop:disable Metrics/MethodLength
62
73
  def properties(cop)
63
74
  header = [
64
- 'Enabled by default', 'Safe', 'Supports autocorrection', 'VersionAdded',
65
- 'VersionChanged'
75
+ 'Enabled by default', 'Safe', 'Supports autocorrection', 'Version Added',
76
+ 'Version Changed'
66
77
  ]
67
78
  autocorrect = if cop.support_autocorrect?
68
79
  "Yes#{' (Unsafe)' unless cop.new(config).safe_autocorrect?}"
@@ -217,12 +228,13 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
217
228
  ]
218
229
  pars = cop_config.reject { |k| non_display_keys.include? k }
219
230
  description = 'No documentation'
220
- examples_object = []
231
+ examples_object = safety_object = []
221
232
  cop_code(cop) do |code_object|
222
233
  description = code_object.docstring unless code_object.docstring.blank?
223
234
  examples_object = code_object.tags('example')
235
+ safety_object = code_object.tags('safety')
224
236
  end
225
- cops_body(cop, description, examples_object, pars)
237
+ cops_body(cop, description, examples_object, safety_object, pars)
226
238
  end
227
239
 
228
240
  def cop_code(cop)