rubocop 1.20.0 → 1.22.2

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -33,6 +33,27 @@ module RuboCop
33
33
  # g = ( a + 3 )
34
34
  # y()
35
35
  #
36
+ # @example EnforcedStyle: compact
37
+ # # The `compact` style enforces that parentheses have a space at the
38
+ # # beginning with the exception that successive parentheses are allowed.
39
+ # # Note: Empty parentheses should not have spaces.
40
+ #
41
+ # # bad
42
+ # f(3)
43
+ # g = (a + 3)
44
+ # y( )
45
+ # g( f( x ) )
46
+ # g( f( x( 3 ) ), 5 )
47
+ # g( ( ( 3 + 5 ) * f) ** x, 5 )
48
+ #
49
+ # # good
50
+ # f( 3 )
51
+ # g = ( a + 3 )
52
+ # y()
53
+ # g( f( x ))
54
+ # g( f( x( 3 )), 5 )
55
+ # g((( 3 + 5 ) * f ) ** x, 5 )
56
+ #
36
57
  class SpaceInsideParens < Base
37
58
  include SurroundingSpace
38
59
  include RangeHelp
@@ -45,14 +66,13 @@ module RuboCop
45
66
  def on_new_investigation
46
67
  tokens = processed_source.sorted_tokens
47
68
 
48
- if style == :space
69
+ case style
70
+ when :space
49
71
  process_with_space_style(tokens)
72
+ when :compact
73
+ process_with_compact_style(tokens)
50
74
  else
51
- each_extraneous_space(tokens) do |range|
52
- add_offense(range) do |corrector|
53
- corrector.remove(range)
54
- end
55
- end
75
+ correct_extraneous_space(tokens)
56
76
  end
57
77
  end
58
78
 
@@ -60,20 +80,23 @@ module RuboCop
60
80
 
61
81
  def process_with_space_style(tokens)
62
82
  tokens.each_cons(2) do |token1, token2|
63
- each_extraneous_space_in_empty_parens(token1, token2) do |range|
64
- add_offense(range) do |corrector|
65
- corrector.remove(range)
66
- end
67
- end
68
- each_missing_space(token1, token2) do |range|
69
- add_offense(range, message: MSG_SPACE) do |corrector|
70
- corrector.insert_before(range, ' ')
71
- end
83
+ correct_extraneous_space_in_empty_parens(token1, token2)
84
+ correct_missing_space(token1, token2)
85
+ end
86
+ end
87
+
88
+ def process_with_compact_style(tokens)
89
+ tokens.each_cons(2) do |token1, token2|
90
+ correct_extraneous_space_in_empty_parens(token1, token2)
91
+ if !left_parens?(token1, token2) && !right_parens?(token1, token2)
92
+ correct_missing_space(token1, token2)
93
+ else
94
+ correct_extaneus_space_between_consecutive_parens(token1, token2)
72
95
  end
73
96
  end
74
97
  end
75
98
 
76
- def each_extraneous_space(tokens)
99
+ def correct_extraneous_space(tokens)
77
100
  tokens.each_cons(2) do |token1, token2|
78
101
  next unless parens?(token1, token2)
79
102
 
@@ -82,36 +105,59 @@ module RuboCop
82
105
  next if token2.comment?
83
106
  next unless same_line?(token1, token2) && token1.space_after?
84
107
 
85
- yield range_between(token1.end_pos, token2.begin_pos)
108
+ range = range_between(token1.end_pos, token2.begin_pos)
109
+ add_offense(range) do |corrector|
110
+ corrector.remove(range)
111
+ end
112
+ end
113
+ end
114
+
115
+ def correct_extaneus_space_between_consecutive_parens(token1, token2)
116
+ return if range_between(token1.end_pos, token2.begin_pos).source != ' '
117
+
118
+ range = range_between(token1.end_pos, token2.begin_pos)
119
+ add_offense(range) do |corrector|
120
+ corrector.remove(range)
86
121
  end
87
122
  end
88
123
 
89
- def each_extraneous_space_in_empty_parens(token1, token2)
124
+ def correct_extraneous_space_in_empty_parens(token1, token2)
90
125
  return unless token1.left_parens? && token2.right_parens?
91
126
 
92
127
  return if range_between(token1.begin_pos, token2.end_pos).source == '()'
93
128
 
94
- yield range_between(token1.end_pos, token2.begin_pos)
129
+ range = range_between(token1.end_pos, token2.begin_pos)
130
+ add_offense(range) do |corrector|
131
+ corrector.remove(range)
132
+ end
95
133
  end
96
134
 
97
- def each_missing_space(token1, token2)
135
+ def correct_missing_space(token1, token2)
98
136
  return if can_be_ignored?(token1, token2)
99
137
 
100
- if token1.left_parens?
101
- yield range_between(token2.begin_pos, token2.begin_pos + 1)
102
- elsif token2.right_parens?
103
- yield range_between(token2.begin_pos, token2.end_pos)
104
- end
105
- end
138
+ range = if token1.left_parens?
139
+ range_between(token2.begin_pos, token2.begin_pos + 1)
140
+ elsif token2.right_parens?
141
+ range_between(token2.begin_pos, token2.end_pos)
142
+ end
106
143
 
107
- def same_line?(token1, token2)
108
- token1.line == token2.line
144
+ add_offense(range, message: MSG_SPACE) do |corrector|
145
+ corrector.insert_before(range, ' ')
146
+ end
109
147
  end
110
148
 
111
149
  def parens?(token1, token2)
112
150
  token1.left_parens? || token2.right_parens?
113
151
  end
114
152
 
153
+ def left_parens?(token1, token2)
154
+ token1.left_parens? && token2.left_parens?
155
+ end
156
+
157
+ def right_parens?(token1, token2)
158
+ token1.right_parens? && token2.right_parens?
159
+ end
160
+
115
161
  def can_be_ignored?(token1, token2)
116
162
  return true unless parens?(token1, token2)
117
163
 
@@ -107,7 +107,7 @@ module RuboCop
107
107
  current_token = tokens.reverse.find(&:left_ref_bracket?)
108
108
  previous_token = previous_token(current_token)
109
109
 
110
- if node.method?(:[]=) || previous_token && !previous_token.right_bracket?
110
+ if node.method?(:[]=) || (previous_token && !previous_token.right_bracket?)
111
111
  tokens.find(&:left_ref_bracket?)
112
112
  else
113
113
  current_token
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # This cop looks for expressions containing multiple binary operators
7
+ # where precedence is ambiguous due to lack of parentheses. For example,
8
+ # in `1 + 2 * 3`, the multiplication will happen before the addition, but
9
+ # lexically it appears that the addition will happen first.
10
+ #
11
+ # The cop does not consider unary operators (ie. `!a` or `-b`) or comparison
12
+ # operators (ie. `a =~ b`) because those are not ambiguous.
13
+ #
14
+ # NOTE: Ranges are handled by `Lint/AmbiguousRange`.
15
+ #
16
+ # @example
17
+ # # bad
18
+ # a + b * c
19
+ # a || b && c
20
+ # a ** b + c
21
+ #
22
+ # # good (different precedence)
23
+ # a + (b * c)
24
+ # a || (b && c)
25
+ # (a ** b) + c
26
+ #
27
+ # # good (same precedence)
28
+ # a + b + c
29
+ # a * b / c % d
30
+ class AmbiguousOperatorPrecedence < Base
31
+ extend AutoCorrector
32
+
33
+ # See https://ruby-doc.org/core-3.0.2/doc/syntax/precedence_rdoc.html
34
+ PRECEDENCE = [
35
+ %i[**],
36
+ %i[* / %],
37
+ %i[+ -],
38
+ %i[<< >>],
39
+ %i[&],
40
+ %i[| ^],
41
+ %i[&&],
42
+ %i[||]
43
+ ].freeze
44
+ RESTRICT_ON_SEND = PRECEDENCE.flatten.freeze
45
+ MSG = 'Wrap expressions with varying precedence with parentheses to avoid ambiguity.'
46
+
47
+ def on_new_investigation
48
+ # Cache the precedence of each node being investigated
49
+ # so that we only need to calculate it once
50
+ @node_precedences = {}
51
+ super
52
+ end
53
+
54
+ def on_and(node)
55
+ return unless (parent = node.parent)
56
+
57
+ return if parent.begin_type? # if the `and` is in a `begin`, it's parenthesized already
58
+ return unless parent.or_type?
59
+
60
+ add_offense(node) do |corrector|
61
+ autocorrect(corrector, node)
62
+ end
63
+ end
64
+
65
+ def on_send(node)
66
+ return if node.parenthesized?
67
+
68
+ return unless (parent = node.parent)
69
+ return unless operator?(parent)
70
+ return unless greater_precedence?(node, parent)
71
+
72
+ add_offense(node) do |corrector|
73
+ autocorrect(corrector, node)
74
+ end
75
+ end
76
+
77
+ private
78
+
79
+ def precedence(node)
80
+ @node_precedences.fetch(node) do
81
+ PRECEDENCE.index { |operators| operators.include?(operator_name(node)) }
82
+ end
83
+ end
84
+
85
+ def operator?(node)
86
+ (node.send_type? && RESTRICT_ON_SEND.include?(node.method_name)) || node.operator_keyword?
87
+ end
88
+
89
+ def greater_precedence?(node1, node2)
90
+ node1_precedence = precedence(node1)
91
+ node2_precedence = precedence(node2)
92
+ return false unless node1_precedence && node2_precedence
93
+
94
+ node2_precedence > node1_precedence
95
+ end
96
+
97
+ def operator_name(node)
98
+ if node.send_type?
99
+ node.method_name
100
+ else
101
+ node.operator.to_sym
102
+ end
103
+ end
104
+
105
+ def autocorrect(corrector, node)
106
+ corrector.wrap(node, '(', ')')
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -10,12 +10,6 @@ module RuboCop
10
10
  # explicit by requiring parenthesis around complex range boundaries (anything
11
11
  # that is not a basic literal: numerics, strings, symbols, etc.).
12
12
  #
13
- # NOTE: The cop auto-corrects by wrapping the entire boundary in parentheses, which
14
- # makes the outcome more explicit but is possible to not be the intention of the
15
- # programmer. For this reason, this cop's auto-correct is marked as unsafe (it
16
- # will not change the behaviour of the code, but will not necessarily match the
17
- # intent of the program).
18
- #
19
13
  # This cop can be configured with `RequireParenthesesForMethodChains` in order to
20
14
  # specify whether method chains (including `self.foo`) should be wrapped in parens
21
15
  # by this cop.
@@ -23,6 +17,13 @@ module RuboCop
23
17
  # NOTE: Regardless of this configuration, if a method receiver is a basic literal
24
18
  # value, it will be wrapped in order to prevent the ambiguity of `1..2.to_a`.
25
19
  #
20
+ # @safety
21
+ # The cop auto-corrects by wrapping the entire boundary in parentheses, which
22
+ # makes the outcome more explicit but is possible to not be the intention of the
23
+ # programmer. For this reason, this cop's auto-correct is unsafe (it will not
24
+ # change the behaviour of the code, but will not necessarily match the
25
+ # intent of the program).
26
+ #
26
27
  # @example
27
28
  # # bad
28
29
  # x || 1..2
@@ -55,7 +56,6 @@ module RuboCop
55
56
  #
56
57
  # # good
57
58
  # (a.foo)..(b.bar)
58
- #
59
59
  class AmbiguousRange < Base
60
60
  extend AutoCorrector
61
61
 
@@ -82,8 +82,8 @@ module RuboCop
82
82
  def acceptable?(node)
83
83
  node.begin_type? ||
84
84
  node.basic_literal? ||
85
- node.variable? || node.const_type? ||
86
- node.call_type? && acceptable_call?(node)
85
+ node.variable? || node.const_type? || node.self_type? ||
86
+ (node.call_type? && acceptable_call?(node))
87
87
  end
88
88
 
89
89
  def acceptable_call?(node)
@@ -49,7 +49,7 @@ module RuboCop
49
49
  def on_if(node)
50
50
  return if node.condition.block_type?
51
51
 
52
- traverse_node(node.condition, ASGN_TYPES) do |asgn_node|
52
+ traverse_node(node.condition) do |asgn_node|
53
53
  next :skip_children if skip_children?(asgn_node)
54
54
  next if allowed_construct?(asgn_node)
55
55
 
@@ -83,13 +83,15 @@ module RuboCop
83
83
  (safe_assignment_allowed? && safe_assignment?(asgn_node))
84
84
  end
85
85
 
86
- # each_node/visit_descendants_with_types with :skip_children
87
- def traverse_node(node, types, &block)
88
- result = yield node if types.include?(node.type)
86
+ def traverse_node(node, &block)
87
+ # if the node is a block, any assignments are irrelevant
88
+ return if node.block_type?
89
+
90
+ result = yield node if ASGN_TYPES.include?(node.type)
89
91
 
90
92
  return if result == :skip_children
91
93
 
92
- node.each_child_node { |child| traverse_node(child, types, &block) }
94
+ node.each_child_node { |child| traverse_node(child, &block) }
93
95
  end
94
96
  end
95
97
  end
@@ -5,15 +5,28 @@ module RuboCop
5
5
  module Lint
6
6
  # This cop checks for places where binary operator has identical operands.
7
7
  #
8
- # It covers arithmetic operators: `+`, `-`, `*`, `/`, `%`, `**`;
8
+ # It covers arithmetic operators: `-`, `/`, `%`;
9
9
  # comparison operators: `==`, `===`, `=~`, `>`, `>=`, `<`, `<=`;
10
- # bitwise operators: `|`, `^`, `&`, `<<`, `>>`;
10
+ # bitwise operators: `|`, `^`, `&`;
11
11
  # boolean operators: `&&`, `||`
12
12
  # and "spaceship" operator - `<=>`.
13
13
  #
14
- # This cop is marked as unsafe as it does not consider side effects when calling methods
15
- # and thus can generate false positives:
14
+ # Simple arithmetic operations are allowed by this cop: `+`, `*`, `**`, `<<` and `>>`.
15
+ # Although these can be rewritten in a different way, it should not be necessary to
16
+ # do so. This does not include operations such as `-` or `/` where the result will
17
+ # always be the same (`x - x` will always be 0; `x / x` will always be 1), and
18
+ # thus are legitimate offenses.
19
+ #
20
+ # @safety
21
+ # This cop is unsafe as it does not consider side effects when calling methods
22
+ # and thus can generate false positives, for example:
23
+ #
24
+ # [source,ruby]
25
+ # ----
16
26
  # if wr.take_char == '\0' && wr.take_char == '\0'
27
+ # # ...
28
+ # end
29
+ # ----
17
30
  #
18
31
  # @example
19
32
  # # bad
@@ -24,7 +37,7 @@ module RuboCop
24
37
  # do_something
25
38
  # end
26
39
  #
27
- # def childs?
40
+ # def child?
28
41
  # left_child || left_child
29
42
  # end
30
43
  #
@@ -6,6 +6,11 @@ module RuboCop
6
6
  # This cop checks for `:true` and `:false` symbols.
7
7
  # In most cases it would be a typo.
8
8
  #
9
+ # @safety
10
+ # Autocorrection is unsafe for this cop because code relying
11
+ # on `:true` or `:false` symbols will break when those are
12
+ # changed to actual booleans.
13
+ #
9
14
  # @example
10
15
  #
11
16
  # # bad
@@ -57,8 +57,6 @@ module RuboCop
57
57
  class Debugger < Base
58
58
  MSG = 'Remove debugger entry point `%<source>s`.'
59
59
 
60
- RESTRICT_ON_SEND = [].freeze
61
-
62
60
  # @!method kernel?(node)
63
61
  def_node_matcher :kernel?, <<~PATTERN
64
62
  (const {nil? cbase} :Kernel)
@@ -58,12 +58,12 @@ module RuboCop
58
58
  end
59
59
 
60
60
  def to_s
61
- [class_constant, method].compact.join(delimeter)
61
+ [class_constant, method].compact.join(delimiter)
62
62
  end
63
63
 
64
64
  private
65
65
 
66
- def delimeter
66
+ def delimiter
67
67
  CLASS_METHOD_DELIMETER
68
68
  end
69
69
  end
@@ -81,12 +81,12 @@ module RuboCop
81
81
  end
82
82
 
83
83
  def to_s
84
- [class_constant, method].compact.join(delimeter)
84
+ [class_constant, method].compact.join(delimiter)
85
85
  end
86
86
 
87
87
  private
88
88
 
89
- def delimeter
89
+ def delimiter
90
90
  instance_method? ? INSTANCE_METHOD_DELIMETER : CLASS_METHOD_DELIMETER
91
91
  end
92
92
 
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # This cop checks constructors for disjunctive assignments that should
6
+ # This cop checks constructors for disjunctive assignments (`||=`) that should
7
7
  # be plain assignments.
8
8
  #
9
9
  # So far, this cop is only concerned with disjunctive assignment of
@@ -12,6 +12,29 @@ module RuboCop
12
12
  # In ruby, an instance variable is nil until a value is assigned, so the
13
13
  # disjunction is unnecessary. A plain assignment has the same effect.
14
14
  #
15
+ # @safety
16
+ # This cop is unsafe because it can register a false positive when a
17
+ # method is redefined in a subclass that calls super. For example:
18
+ #
19
+ # [source,ruby]
20
+ # ----
21
+ # class Base
22
+ # def initialize
23
+ # @config ||= 'base'
24
+ # end
25
+ # end
26
+ #
27
+ # class Derived < Base
28
+ # def initialize
29
+ # @config = 'derived'
30
+ # super
31
+ # end
32
+ # end
33
+ # ----
34
+ #
35
+ # Without the disjunctive assignment, `Derived` will be unable to override
36
+ # the value for `@config`.
37
+ #
15
38
  # @example
16
39
  # # bad
17
40
  # def initialize
@@ -49,6 +49,9 @@ module RuboCop
49
49
  def on_if(node)
50
50
  return if node.ternary?
51
51
 
52
+ # If the if is on a single line, it'll be handled by `Style/OneLineConditional`
53
+ return if node.single_line?
54
+
52
55
  check(node)
53
56
  end
54
57
 
@@ -66,13 +69,10 @@ module RuboCop
66
69
 
67
70
  def check_else(node)
68
71
  else_branch = node.else_branch
69
-
70
- return unless else_branch.begin_type?
71
-
72
- first_else = else_branch.children.first
72
+ first_else = else_branch.begin_type? ? else_branch.children.first : else_branch
73
73
 
74
74
  return unless first_else
75
- return unless first_else.source_range.line == node.loc.else.line
75
+ return unless same_line?(first_else, node.loc.else)
76
76
 
77
77
  add_offense(first_else) { |corrector| autocorrect(corrector, node, first_else) }
78
78
  end
@@ -81,9 +81,13 @@ module RuboCop
81
81
  corrector.insert_after(node.loc.else, "\n")
82
82
 
83
83
  blank_range = range_between(node.loc.else.end_pos, first_else.loc.expression.begin_pos)
84
- indentation = indent(node.else_branch.children[1])
84
+ indentation = indent(node, offset: indentation_width)
85
85
  corrector.replace(blank_range, indentation)
86
86
  end
87
+
88
+ def indentation_width
89
+ @config.for_cop('Layout/IndentationWidth')['Width'] || 2
90
+ end
87
91
  end
88
92
  end
89
93
  end
@@ -51,7 +51,7 @@ module RuboCop
51
51
 
52
52
  def on_case_match(node)
53
53
  node.in_pattern_branches.each do |branch|
54
- next if branch.body || cop_config['AllowComments'] && comment_lines?(node)
54
+ next if branch.body || (cop_config['AllowComments'] && comment_lines?(node))
55
55
 
56
56
  add_offense(branch)
57
57
  end
@@ -118,7 +118,7 @@ module RuboCop
118
118
  end
119
119
 
120
120
  def correct_arguments?(arguments)
121
- arguments.size == 1 || arguments.size == 2 && arguments[1].hash_type?
121
+ arguments.size == 1 || (arguments.size == 2 && arguments[1].hash_type?)
122
122
  end
123
123
 
124
124
  def build_kwargs(node)
@@ -24,7 +24,7 @@ module RuboCop
24
24
  def on_float(node)
25
25
  value, = *node
26
26
 
27
- return unless value.infinite? || value.zero? && /[1-9]/.match?(node.source)
27
+ return unless value.infinite? || (value.zero? && /[1-9]/.match?(node.source))
28
28
 
29
29
  add_offense(node)
30
30
  end
@@ -3,10 +3,19 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # Prefer using `Hash#compare_by_identity` than using `object_id` for hash keys.
6
+ # Prefer using `Hash#compare_by_identity` rather than using `object_id`
7
+ # for hash keys.
7
8
  #
8
- # This cop is marked as unsafe as a hash possibly can contain other keys
9
- # besides `object_id`s.
9
+ # This cop looks for hashes being keyed by objects' `object_id`, using
10
+ # one of these methods: `key?`, `has_key?`, `fetch`, `[]` and `[]=`.
11
+ #
12
+ # @safety
13
+ # This cop is unsafe. Although unlikely, the hash could store both object
14
+ # ids and other values that need be compared by value, and thus
15
+ # could be a false positive.
16
+ #
17
+ # Furthermore, this cop cannot guarantee that the receiver of one of the
18
+ # methods (`key?`, etc.) is actually a hash.
10
19
  #
11
20
  # @example
12
21
  # # bad
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ #
7
+ # This cop checks for `IO.select` that is incompatible with Fiber Scheduler since Ruby 3.0.
8
+ #
9
+ # @example
10
+ #
11
+ # # bad
12
+ # IO.select([io], [], [], timeout)
13
+ #
14
+ # # good
15
+ # io.wait_readable(timeout)
16
+ #
17
+ # # bad
18
+ # IO.select([], [io], [], timeout)
19
+ #
20
+ # # good
21
+ # io.wait_writable(timeout)
22
+ #
23
+ class IncompatibleIoSelectWithFiberScheduler < Base
24
+ extend AutoCorrector
25
+
26
+ MSG = 'Use `%<preferred>s` instead of `%<current>s`.'
27
+ RESTRICT_ON_SEND = %i[select].freeze
28
+
29
+ # @!method io_select(node)
30
+ def_node_matcher :io_select, <<~PATTERN
31
+ (send
32
+ (const {nil? cbase} :IO) :select $_ $_ {(array) nil} $...)
33
+ PATTERN
34
+
35
+ def on_send(node)
36
+ return unless (read, write, timeout = io_select(node))
37
+ return unless scheduler_compatible?(read, write) || scheduler_compatible?(write, read)
38
+
39
+ preferred = preferred_method(read, write, timeout)
40
+ message = format(MSG, preferred: preferred, current: node.source)
41
+
42
+ add_offense(node, message: message) do |corrector|
43
+ corrector.replace(node, preferred)
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def scheduler_compatible?(io1, io2)
50
+ return false unless io1.array_type? && io1.values.size == 1
51
+
52
+ io2.array_type? ? io2.values.empty? : io2.nil_type?
53
+ end
54
+
55
+ def preferred_method(read, write, timeout)
56
+ timeout_argument = timeout.empty? ? '' : "(#{timeout[0].source})"
57
+
58
+ if read.array_type? && read.values[0]
59
+ "#{read.values[0].source}.wait_readable#{timeout_argument}"
60
+ else
61
+ "#{write.values[0].source}.wait_writable#{timeout_argument}"
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -5,6 +5,11 @@ module RuboCop
5
5
  module Lint
6
6
  # This cop checks for interpolation in a single quoted string.
7
7
  #
8
+ # @safety
9
+ # This cop is generally safe, but is marked as unsafe because
10
+ # it is possible to actually intentionally have text inside
11
+ # `#{...}` in a single quoted string.
12
+ #
8
13
  # @example
9
14
  #
10
15
  # # bad
@@ -5,9 +5,10 @@ module RuboCop
5
5
  module Lint
6
6
  # This cop checks for uses of `begin...end while/until something`.
7
7
  #
8
- # The cop is marked as unsafe because behaviour can change in some cases, including
9
- # if a local variable inside the loop body is accessed outside of it, or if the
10
- # loop body raises a `StopIteration` exception (which `Kernel#loop` rescues).
8
+ # @safety
9
+ # The cop is unsafe because behaviour can change in some cases, including
10
+ # if a local variable inside the loop body is accessed outside of it, or if the
11
+ # loop body raises a `StopIteration` exception (which `Kernel#loop` rescues).
11
12
  #
12
13
  # @example
13
14
  #