rubocop 1.20.0 → 1.22.2

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 (204) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +64 -15
  4. data/lib/rubocop/config.rb +5 -0
  5. data/lib/rubocop/config_loader.rb +3 -1
  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_comment.rb +3 -3
  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 +31 -24
  16. data/lib/rubocop/cop/generator.rb +14 -8
  17. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +60 -0
  18. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -1
  19. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  20. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
  21. data/lib/rubocop/cop/layout/assignment_indentation.rb +1 -1
  22. data/lib/rubocop/cop/layout/class_structure.rb +2 -1
  23. data/lib/rubocop/cop/layout/dot_position.rb +30 -5
  24. data/lib/rubocop/cop/layout/empty_comment.rb +1 -1
  25. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +7 -4
  26. data/lib/rubocop/cop/layout/end_alignment.rb +2 -3
  27. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +1 -1
  28. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +1 -1
  29. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +1 -1
  30. data/lib/rubocop/cop/layout/hash_alignment.rb +1 -1
  31. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +1 -1
  32. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  33. data/lib/rubocop/cop/layout/leading_comment_space.rb +1 -1
  34. data/lib/rubocop/cop/layout/line_length.rb +9 -7
  35. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +1 -1
  36. data/lib/rubocop/cop/layout/multiline_block_layout.rb +3 -3
  37. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +1 -1
  38. data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -0
  39. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +1 -1
  40. data/lib/rubocop/cop/layout/single_line_block_chain.rb +15 -4
  41. data/lib/rubocop/cop/layout/space_after_not.rb +1 -0
  42. data/lib/rubocop/cop/layout/space_around_equals_in_parameter_default.rb +2 -1
  43. data/lib/rubocop/cop/layout/space_around_keyword.rb +2 -2
  44. data/lib/rubocop/cop/layout/space_before_brackets.rb +1 -0
  45. data/lib/rubocop/cop/layout/space_before_comment.rb +1 -1
  46. data/lib/rubocop/cop/layout/space_inside_parens.rb +74 -28
  47. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +1 -1
  48. data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +111 -0
  49. data/lib/rubocop/cop/lint/ambiguous_range.rb +9 -9
  50. data/lib/rubocop/cop/lint/assignment_in_condition.rb +7 -5
  51. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +18 -5
  52. data/lib/rubocop/cop/lint/boolean_symbol.rb +5 -0
  53. data/lib/rubocop/cop/lint/debugger.rb +0 -2
  54. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +4 -4
  55. data/lib/rubocop/cop/lint/disjunctive_assignment_in_constructor.rb +24 -1
  56. data/lib/rubocop/cop/lint/else_layout.rb +10 -6
  57. data/lib/rubocop/cop/lint/empty_in_pattern.rb +1 -1
  58. data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
  59. data/lib/rubocop/cop/lint/float_out_of_range.rb +1 -1
  60. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +12 -3
  61. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +67 -0
  62. data/lib/rubocop/cop/lint/interpolation_check.rb +5 -0
  63. data/lib/rubocop/cop/lint/loop.rb +4 -3
  64. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +5 -1
  65. data/lib/rubocop/cop/lint/number_conversion.rb +12 -1
  66. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -1
  67. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +4 -2
  68. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +17 -0
  69. data/lib/rubocop/cop/lint/percent_string_array.rb +10 -0
  70. data/lib/rubocop/cop/lint/raise_exception.rb +4 -0
  71. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +5 -4
  72. data/lib/rubocop/cop/lint/require_relative_self_path.rb +50 -0
  73. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +1 -1
  74. data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
  75. data/lib/rubocop/cop/lint/triple_quotes.rb +1 -1
  76. data/lib/rubocop/cop/lint/unexpected_block_arity.rb +8 -3
  77. data/lib/rubocop/cop/lint/unused_method_argument.rb +2 -3
  78. data/lib/rubocop/cop/lint/useless_method_definition.rb +3 -2
  79. data/lib/rubocop/cop/lint/useless_setter_call.rb +7 -4
  80. data/lib/rubocop/cop/lint/useless_times.rb +4 -3
  81. data/lib/rubocop/cop/metrics/abc_size.rb +6 -0
  82. data/lib/rubocop/cop/metrics/parameter_lists.rb +5 -2
  83. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
  84. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +1 -1
  85. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  86. data/lib/rubocop/cop/mixin/code_length.rb +1 -1
  87. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -2
  88. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +15 -6
  89. data/lib/rubocop/cop/mixin/hash_transform_method.rb +3 -3
  90. data/lib/rubocop/cop/mixin/heredoc.rb +1 -3
  91. data/lib/rubocop/cop/mixin/multiline_element_indentation.rb +1 -1
  92. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +2 -2
  93. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +1 -1
  94. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +9 -1
  95. data/lib/rubocop/cop/mixin/percent_array.rb +6 -1
  96. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +9 -1
  97. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +1 -1
  98. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  99. data/lib/rubocop/cop/mixin/statement_modifier.rb +1 -1
  100. data/lib/rubocop/cop/mixin/string_literals_help.rb +5 -1
  101. data/lib/rubocop/cop/mixin/trailing_body.rb +1 -1
  102. data/lib/rubocop/cop/naming/ascii_identifiers.rb +0 -3
  103. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  104. data/lib/rubocop/cop/naming/constant_name.rb +1 -1
  105. data/lib/rubocop/cop/naming/inclusive_language.rb +9 -9
  106. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +5 -4
  107. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +7 -0
  108. data/lib/rubocop/cop/security/io_methods.rb +49 -0
  109. data/lib/rubocop/cop/security/json_load.rb +8 -7
  110. data/lib/rubocop/cop/security/open.rb +4 -0
  111. data/lib/rubocop/cop/security/yaml_load.rb +4 -0
  112. data/lib/rubocop/cop/style/accessor_grouping.rb +2 -2
  113. data/lib/rubocop/cop/style/and_or.rb +5 -0
  114. data/lib/rubocop/cop/style/arguments_forwarding.rb +13 -2
  115. data/lib/rubocop/cop/style/array_coercion.rb +21 -3
  116. data/lib/rubocop/cop/style/ascii_comments.rb +0 -3
  117. data/lib/rubocop/cop/style/case_equality.rb +6 -9
  118. data/lib/rubocop/cop/style/case_like_if.rb +5 -0
  119. data/lib/rubocop/cop/style/class_and_module_children.rb +9 -0
  120. data/lib/rubocop/cop/style/collection_compact.rb +7 -5
  121. data/lib/rubocop/cop/style/collection_methods.rb +8 -6
  122. data/lib/rubocop/cop/style/combinable_loops.rb +3 -2
  123. data/lib/rubocop/cop/style/commented_keyword.rb +9 -4
  124. data/lib/rubocop/cop/style/date_time.rb +5 -0
  125. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +1 -1
  126. data/lib/rubocop/cop/style/documentation.rb +23 -8
  127. data/lib/rubocop/cop/style/double_negation.rb +15 -5
  128. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  129. data/lib/rubocop/cop/style/explicit_block_argument.rb +21 -11
  130. data/lib/rubocop/cop/style/float_division.rb +10 -2
  131. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +6 -1
  132. data/lib/rubocop/cop/style/global_std_stream.rb +4 -0
  133. data/lib/rubocop/cop/style/hash_each_methods.rb +5 -0
  134. data/lib/rubocop/cop/style/hash_transform_keys.rb +4 -6
  135. data/lib/rubocop/cop/style/hash_transform_values.rb +4 -6
  136. data/lib/rubocop/cop/style/identical_conditional_branches.rb +18 -16
  137. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +18 -4
  138. data/lib/rubocop/cop/style/infinite_loop.rb +4 -3
  139. data/lib/rubocop/cop/style/inverse_methods.rb +9 -2
  140. data/lib/rubocop/cop/style/lambda_call.rb +1 -1
  141. data/lib/rubocop/cop/style/line_end_concatenation.rb +14 -1
  142. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +6 -6
  143. data/lib/rubocop/cop/style/module_function.rb +8 -9
  144. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +1 -1
  145. data/lib/rubocop/cop/style/multiline_when_then.rb +1 -1
  146. data/lib/rubocop/cop/style/mutable_constant.rb +13 -8
  147. data/lib/rubocop/cop/style/negated_if.rb +1 -1
  148. data/lib/rubocop/cop/style/negated_unless.rb +1 -1
  149. data/lib/rubocop/cop/style/non_nil_check.rb +2 -2
  150. data/lib/rubocop/cop/style/not.rb +2 -2
  151. data/lib/rubocop/cop/style/numbered_parameters.rb +46 -0
  152. data/lib/rubocop/cop/style/numbered_parameters_limit.rb +50 -0
  153. data/lib/rubocop/cop/style/numeric_literals.rb +7 -8
  154. data/lib/rubocop/cop/style/numeric_predicate.rb +5 -0
  155. data/lib/rubocop/cop/style/optional_arguments.rb +4 -0
  156. data/lib/rubocop/cop/style/optional_boolean_parameter.rb +14 -4
  157. data/lib/rubocop/cop/style/parallel_assignment.rb +1 -1
  158. data/lib/rubocop/cop/style/percent_q_literals.rb +2 -2
  159. data/lib/rubocop/cop/style/preferred_hash_methods.rb +9 -4
  160. data/lib/rubocop/cop/style/quoted_symbols.rb +10 -6
  161. data/lib/rubocop/cop/style/raise_args.rb +1 -1
  162. data/lib/rubocop/cop/style/redundant_argument.rb +19 -9
  163. data/lib/rubocop/cop/style/redundant_condition.rb +2 -3
  164. data/lib/rubocop/cop/style/redundant_fetch_block.rb +4 -0
  165. data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +12 -3
  166. data/lib/rubocop/cop/style/redundant_freeze.rb +0 -1
  167. data/lib/rubocop/cop/style/redundant_interpolation.rb +1 -1
  168. data/lib/rubocop/cop/style/redundant_percent_q.rb +2 -3
  169. data/lib/rubocop/cop/style/redundant_self.rb +10 -0
  170. data/lib/rubocop/cop/style/redundant_self_assignment.rb +4 -3
  171. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +1 -1
  172. data/lib/rubocop/cop/style/redundant_sort.rb +51 -18
  173. data/lib/rubocop/cop/style/regexp_literal.rb +3 -3
  174. data/lib/rubocop/cop/style/return_nil.rb +2 -1
  175. data/lib/rubocop/cop/style/safe_navigation.rb +13 -2
  176. data/lib/rubocop/cop/style/select_by_regexp.rb +138 -0
  177. data/lib/rubocop/cop/style/single_argument_dig.rb +5 -0
  178. data/lib/rubocop/cop/style/slicing_with_range.rb +13 -0
  179. data/lib/rubocop/cop/style/special_global_vars.rb +4 -0
  180. data/lib/rubocop/cop/style/static_class.rb +5 -5
  181. data/lib/rubocop/cop/style/string_chars.rb +4 -2
  182. data/lib/rubocop/cop/style/string_concatenation.rb +5 -1
  183. data/lib/rubocop/cop/style/string_hash_keys.rb +4 -0
  184. data/lib/rubocop/cop/style/struct_inheritance.rb +3 -2
  185. data/lib/rubocop/cop/style/swap_values.rb +4 -2
  186. data/lib/rubocop/cop/style/symbol_proc.rb +26 -0
  187. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +19 -0
  188. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  189. data/lib/rubocop/cop/style/yoda_condition.rb +24 -7
  190. data/lib/rubocop/cop/style/zero_length_predicate.rb +6 -0
  191. data/lib/rubocop/cop/util.rb +15 -4
  192. data/lib/rubocop/cops_documentation_generator.rb +17 -5
  193. data/lib/rubocop/options.rb +126 -112
  194. data/lib/rubocop/rake_task.rb +1 -1
  195. data/lib/rubocop/result_cache.rb +3 -3
  196. data/lib/rubocop/rspec/cop_helper.rb +1 -1
  197. data/lib/rubocop/rspec/expect_offense.rb +6 -2
  198. data/lib/rubocop/rspec/parallel_formatter.rb +90 -0
  199. data/lib/rubocop/rspec/support.rb +1 -0
  200. data/lib/rubocop/runner.rb +2 -3
  201. data/lib/rubocop/target_finder.rb +1 -1
  202. data/lib/rubocop/version.rb +1 -1
  203. data/lib/rubocop.rb +8 -1
  204. metadata +14 -5
@@ -0,0 +1,138 @@
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 Enumerable (array,
7
+ # range, set, etc.; see note below) is calculated based on a `Regexp`
8
+ # match, and suggests `grep` or `grep_v` instead.
9
+ #
10
+ # NOTE: Hashes do not behave as you may expect with `grep`, which
11
+ # means that `hash.grep` is not equivalent to `hash.select`. Although
12
+ # RuboCop is limited by static analysis, this cop attempts to avoid
13
+ # registering an offense when the receiver is a hash (hash literal,
14
+ # `Hash.new`, `Hash#[]`, or `to_h`/`to_hash`).
15
+ #
16
+ # NOTE: `grep` and `grep_v` were optimized when used without a block
17
+ # in Ruby 3.0, but may be slower in previous versions.
18
+ # See https://bugs.ruby-lang.org/issues/17030
19
+ #
20
+ # @safety
21
+ # Autocorrection is marked as unsafe because `MatchData` will
22
+ # not be created by `grep`, but may have previously been relied
23
+ # upon after the `match?` or `=~` call.
24
+ #
25
+ # Additionally, the cop cannot guarantee that the receiver of
26
+ # `select` or `reject` is actually an array by static analysis,
27
+ # so the correction may not be actually equivalent.
28
+ #
29
+ # @example
30
+ # # bad (select or find_all)
31
+ # array.select { |x| x.match? /regexp/ }
32
+ # array.select { |x| /regexp/.match?(x) }
33
+ # array.select { |x| x =~ /regexp/ }
34
+ # array.select { |x| /regexp/ =~ x }
35
+ #
36
+ # # bad (reject)
37
+ # array.reject { |x| x.match? /regexp/ }
38
+ # array.reject { |x| /regexp/.match?(x) }
39
+ # array.reject { |x| x =~ /regexp/ }
40
+ # array.reject { |x| /regexp/ =~ x }
41
+ #
42
+ # # good
43
+ # array.grep(regexp)
44
+ # array.grep_v(regexp)
45
+ class SelectByRegexp < Base
46
+ extend AutoCorrector
47
+ include RangeHelp
48
+
49
+ MSG = 'Prefer `%<replacement>s` to `%<original_method>s` with a regexp match.'
50
+ RESTRICT_ON_SEND = %i[select find_all reject].freeze
51
+ REPLACEMENTS = { select: 'grep', find_all: 'grep', reject: 'grep_v' }.freeze
52
+ REGEXP_METHODS = %i[match? =~].to_set.freeze
53
+
54
+ # @!method regexp_match?(node)
55
+ def_node_matcher :regexp_match?, <<~PATTERN
56
+ {
57
+ (block send (args (arg $_)) ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
58
+ (numblock send $1 ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
59
+ }
60
+ PATTERN
61
+
62
+ # Returns true if a node appears to return a hash
63
+ # @!method creates_hash?(node)
64
+ def_node_matcher :creates_hash?, <<~PATTERN
65
+ {
66
+ (send (const _ :Hash) {:new :[]} ...)
67
+ (block (send (const _ :Hash) :new ...) ...)
68
+ (send _ { :to_h :to_hash } ...)
69
+ }
70
+ PATTERN
71
+
72
+ # @!method calls_lvar?(node, name)
73
+ def_node_matcher :calls_lvar?, <<~PATTERN
74
+ {
75
+ (send (lvar %1) ...)
76
+ (send ... (lvar %1))
77
+ (match-with-lvasgn regexp (lvar %1))
78
+ }
79
+ PATTERN
80
+
81
+ def on_send(node)
82
+ return unless (block_node = node.block_node)
83
+ return if block_node.body.begin_type?
84
+ return if receiver_allowed?(block_node.receiver)
85
+ return unless (regexp_method_send_node = extract_send_node(block_node))
86
+ return if match_predicate_without_receiver?(regexp_method_send_node)
87
+
88
+ regexp = find_regexp(regexp_method_send_node)
89
+ register_offense(node, block_node, regexp)
90
+ end
91
+
92
+ private
93
+
94
+ def receiver_allowed?(node)
95
+ return false unless node
96
+
97
+ node.hash_type? || creates_hash?(node)
98
+ end
99
+
100
+ def register_offense(node, block_node, regexp)
101
+ replacement = REPLACEMENTS[node.method_name.to_sym]
102
+ message = format(MSG, replacement: replacement, original_method: node.method_name)
103
+
104
+ add_offense(block_node, message: message) do |corrector|
105
+ # Only correct if it can be determined what the regexp is
106
+ if regexp
107
+ range = range_between(node.loc.selector.begin_pos, block_node.loc.end.end_pos)
108
+ corrector.replace(range, "#{replacement}(#{regexp.source})")
109
+ end
110
+ end
111
+ end
112
+
113
+ def extract_send_node(block_node)
114
+ return unless (block_arg_name, regexp_method_send_node = regexp_match?(block_node))
115
+
116
+ block_arg_name = :"_#{block_arg_name}" if block_node.numblock_type?
117
+ return unless calls_lvar?(regexp_method_send_node, block_arg_name)
118
+
119
+ regexp_method_send_node
120
+ end
121
+
122
+ def find_regexp(node)
123
+ return node.child_nodes.first if node.match_with_lvasgn_type?
124
+
125
+ if node.receiver.lvar_type?
126
+ node.first_argument
127
+ elsif node.first_argument.lvar_type?
128
+ node.receiver
129
+ end
130
+ end
131
+
132
+ def match_predicate_without_receiver?(node)
133
+ node.send_type? && node.method?(:match?) && node.receiver.nil?
134
+ end
135
+ end
136
+ end
137
+ end
138
+ 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]
@@ -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,8 +5,9 @@ module RuboCop
5
5
  module Style
6
6
  # This cop checks for inheritance from Struct.new.
7
7
  #
8
- # It is marked as unsafe auto-correction because it will change the
9
- # inheritance tree (e.g. return value of `Module#ancestors`).
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.
10
11
  #
11
12
  # @example
12
13
  # # bad
@@ -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
@@ -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)
@@ -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
@@ -113,7 +113,8 @@ module RuboCop
113
113
  if needs_escaping?(string) && compatible_external_encoding_for?(string)
114
114
  string.inspect
115
115
  else
116
- "'#{string.gsub('\\') { '\\\\' }}'"
116
+ # In a single-quoted strings, double quotes don't need to be escaped
117
+ "'#{string.gsub('\"', '"').gsub('\\') { '\\\\' }}'"
117
118
  end
118
119
  end
119
120
 
@@ -125,12 +126,22 @@ module RuboCop
125
126
  StringInterpreter.interpret(string)
126
127
  end
127
128
 
129
+ def line(node_or_range)
130
+ if node_or_range.respond_to?(:line)
131
+ node_or_range.line
132
+ elsif node_or_range.respond_to?(:loc)
133
+ node_or_range.loc.line
134
+ end
135
+ end
136
+
128
137
  def same_line?(node1, node2)
129
- node1.respond_to?(:loc) && node2.respond_to?(:loc) && node1.loc.line == node2.loc.line
138
+ line1 = line(node1)
139
+ line2 = line(node2)
140
+ line1 && line2 && line1 == line2
130
141
  end
131
142
 
132
- def indent(node)
133
- ' ' * node.loc.column
143
+ def indent(node, offset: 0)
144
+ ' ' * (node.loc.column + offset)
134
145
  end
135
146
 
136
147
  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)