rubocop 1.19.0 → 1.22.0

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