rubocop 1.19.0 → 1.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +129 -21
  4. data/lib/rubocop/config.rb +5 -0
  5. data/lib/rubocop/config_loader.rb +5 -3
  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/gem_filename.rb +103 -0
  10. data/lib/rubocop/cop/bundler/insecure_protocol_source.rb +45 -21
  11. data/lib/rubocop/cop/bundler/ordered_gems.rb +3 -12
  12. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +2 -2
  13. data/lib/rubocop/cop/correctors/line_break_corrector.rb +1 -1
  14. data/lib/rubocop/cop/correctors/ordered_gem_corrector.rb +11 -10
  15. data/lib/rubocop/cop/documentation.rb +1 -1
  16. data/lib/rubocop/cop/gemspec/date_assignment.rb +2 -10
  17. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +1 -10
  18. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +3 -12
  19. data/lib/rubocop/cop/gemspec/require_mfa.rb +146 -0
  20. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +31 -24
  21. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +3 -10
  22. data/lib/rubocop/cop/generator.rb +14 -8
  23. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +60 -0
  24. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -1
  25. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  26. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
  27. data/lib/rubocop/cop/layout/assignment_indentation.rb +1 -1
  28. data/lib/rubocop/cop/layout/block_alignment.rb +3 -3
  29. data/lib/rubocop/cop/layout/class_structure.rb +2 -1
  30. data/lib/rubocop/cop/layout/dot_position.rb +34 -5
  31. data/lib/rubocop/cop/layout/empty_comment.rb +1 -1
  32. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +22 -1
  33. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +7 -4
  34. data/lib/rubocop/cop/layout/end_alignment.rb +2 -3
  35. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +1 -1
  36. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +1 -1
  37. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +1 -1
  38. data/lib/rubocop/cop/layout/hash_alignment.rb +1 -1
  39. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +1 -1
  40. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  41. data/lib/rubocop/cop/layout/leading_comment_space.rb +1 -1
  42. data/lib/rubocop/cop/layout/line_length.rb +9 -7
  43. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +1 -1
  44. data/lib/rubocop/cop/layout/multiline_block_layout.rb +3 -3
  45. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +3 -0
  46. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +1 -1
  47. data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -0
  48. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +6 -5
  49. data/lib/rubocop/cop/layout/single_line_block_chain.rb +15 -4
  50. data/lib/rubocop/cop/layout/space_after_not.rb +1 -0
  51. data/lib/rubocop/cop/layout/space_around_equals_in_parameter_default.rb +2 -1
  52. data/lib/rubocop/cop/layout/space_around_keyword.rb +2 -2
  53. data/lib/rubocop/cop/layout/space_before_brackets.rb +1 -0
  54. data/lib/rubocop/cop/layout/space_before_comment.rb +1 -1
  55. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +11 -5
  56. data/lib/rubocop/cop/layout/space_inside_parens.rb +74 -28
  57. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +1 -1
  58. data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +111 -0
  59. data/lib/rubocop/cop/lint/ambiguous_range.rb +11 -11
  60. data/lib/rubocop/cop/lint/assignment_in_condition.rb +7 -5
  61. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +18 -5
  62. data/lib/rubocop/cop/lint/boolean_symbol.rb +5 -0
  63. data/lib/rubocop/cop/lint/debugger.rb +2 -4
  64. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +4 -4
  65. data/lib/rubocop/cop/lint/deprecated_constants.rb +3 -2
  66. data/lib/rubocop/cop/lint/disjunctive_assignment_in_constructor.rb +24 -1
  67. data/lib/rubocop/cop/lint/else_layout.rb +10 -6
  68. data/lib/rubocop/cop/lint/empty_in_pattern.rb +1 -1
  69. data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
  70. data/lib/rubocop/cop/lint/float_out_of_range.rb +1 -1
  71. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +12 -3
  72. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +67 -0
  73. data/lib/rubocop/cop/lint/interpolation_check.rb +5 -0
  74. data/lib/rubocop/cop/lint/loop.rb +4 -3
  75. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +5 -1
  76. data/lib/rubocop/cop/lint/number_conversion.rb +16 -2
  77. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -1
  78. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +4 -2
  79. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +17 -0
  80. data/lib/rubocop/cop/lint/percent_string_array.rb +10 -0
  81. data/lib/rubocop/cop/lint/raise_exception.rb +4 -0
  82. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +5 -4
  83. data/lib/rubocop/cop/lint/require_relative_self_path.rb +50 -0
  84. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +1 -1
  85. data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
  86. data/lib/rubocop/cop/lint/triple_quotes.rb +1 -1
  87. data/lib/rubocop/cop/lint/unexpected_block_arity.rb +8 -3
  88. data/lib/rubocop/cop/lint/unused_method_argument.rb +2 -3
  89. data/lib/rubocop/cop/lint/useless_method_definition.rb +3 -2
  90. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +117 -0
  91. data/lib/rubocop/cop/lint/useless_setter_call.rb +7 -4
  92. data/lib/rubocop/cop/lint/useless_times.rb +4 -3
  93. data/lib/rubocop/cop/metrics/abc_size.rb +6 -0
  94. data/lib/rubocop/cop/metrics/parameter_lists.rb +5 -2
  95. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
  96. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +1 -1
  97. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  98. data/lib/rubocop/cop/mixin/annotation_comment.rb +57 -34
  99. data/lib/rubocop/cop/mixin/code_length.rb +1 -1
  100. data/lib/rubocop/cop/mixin/documentation_comment.rb +5 -2
  101. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -2
  102. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +23 -1
  103. data/lib/rubocop/cop/mixin/gemspec_help.rb +30 -0
  104. data/lib/rubocop/cop/mixin/hash_transform_method.rb +3 -3
  105. data/lib/rubocop/cop/mixin/heredoc.rb +1 -3
  106. data/lib/rubocop/cop/mixin/multiline_element_indentation.rb +1 -1
  107. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +2 -2
  108. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +1 -1
  109. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +9 -1
  110. data/lib/rubocop/cop/mixin/percent_array.rb +11 -3
  111. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +9 -1
  112. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +1 -1
  113. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  114. data/lib/rubocop/cop/mixin/statement_modifier.rb +1 -1
  115. data/lib/rubocop/cop/mixin/trailing_body.rb +1 -1
  116. data/lib/rubocop/cop/naming/ascii_identifiers.rb +0 -3
  117. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  118. data/lib/rubocop/cop/naming/constant_name.rb +1 -1
  119. data/lib/rubocop/cop/naming/file_name.rb +37 -4
  120. data/lib/rubocop/cop/naming/inclusive_language.rb +9 -9
  121. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +5 -4
  122. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +7 -0
  123. data/lib/rubocop/cop/security/io_methods.rb +49 -0
  124. data/lib/rubocop/cop/security/json_load.rb +8 -7
  125. data/lib/rubocop/cop/security/open.rb +4 -0
  126. data/lib/rubocop/cop/security/yaml_load.rb +4 -0
  127. data/lib/rubocop/cop/style/accessor_grouping.rb +2 -2
  128. data/lib/rubocop/cop/style/and_or.rb +5 -0
  129. data/lib/rubocop/cop/style/arguments_forwarding.rb +13 -2
  130. data/lib/rubocop/cop/style/array_coercion.rb +21 -3
  131. data/lib/rubocop/cop/style/ascii_comments.rb +0 -3
  132. data/lib/rubocop/cop/style/block_delimiters.rb +23 -6
  133. data/lib/rubocop/cop/style/case_equality.rb +6 -9
  134. data/lib/rubocop/cop/style/case_like_if.rb +5 -0
  135. data/lib/rubocop/cop/style/class_and_module_children.rb +9 -0
  136. data/lib/rubocop/cop/style/collection_compact.rb +7 -5
  137. data/lib/rubocop/cop/style/collection_methods.rb +8 -6
  138. data/lib/rubocop/cop/style/combinable_loops.rb +3 -2
  139. data/lib/rubocop/cop/style/comment_annotation.rb +25 -39
  140. data/lib/rubocop/cop/style/commented_keyword.rb +9 -4
  141. data/lib/rubocop/cop/style/date_time.rb +5 -0
  142. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +1 -1
  143. data/lib/rubocop/cop/style/documentation.rb +23 -8
  144. data/lib/rubocop/cop/style/double_negation.rb +27 -6
  145. data/lib/rubocop/cop/style/empty_method.rb +2 -2
  146. data/lib/rubocop/cop/style/encoding.rb +26 -15
  147. data/lib/rubocop/cop/style/explicit_block_argument.rb +21 -11
  148. data/lib/rubocop/cop/style/float_division.rb +10 -2
  149. data/lib/rubocop/cop/style/format_string_token.rb +2 -1
  150. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +7 -2
  151. data/lib/rubocop/cop/style/global_std_stream.rb +4 -0
  152. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +11 -0
  153. data/lib/rubocop/cop/style/hash_each_methods.rb +5 -0
  154. data/lib/rubocop/cop/style/hash_except.rb +4 -3
  155. data/lib/rubocop/cop/style/hash_transform_keys.rb +4 -6
  156. data/lib/rubocop/cop/style/hash_transform_values.rb +4 -6
  157. data/lib/rubocop/cop/style/identical_conditional_branches.rb +18 -16
  158. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +18 -4
  159. data/lib/rubocop/cop/style/infinite_loop.rb +4 -3
  160. data/lib/rubocop/cop/style/inverse_methods.rb +9 -2
  161. data/lib/rubocop/cop/style/lambda_call.rb +1 -1
  162. data/lib/rubocop/cop/style/line_end_concatenation.rb +14 -1
  163. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +6 -6
  164. data/lib/rubocop/cop/style/module_function.rb +8 -9
  165. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +1 -1
  166. data/lib/rubocop/cop/style/multiline_when_then.rb +1 -1
  167. data/lib/rubocop/cop/style/mutable_constant.rb +73 -6
  168. data/lib/rubocop/cop/style/negated_if.rb +1 -1
  169. data/lib/rubocop/cop/style/negated_unless.rb +1 -1
  170. data/lib/rubocop/cop/style/non_nil_check.rb +2 -2
  171. data/lib/rubocop/cop/style/not.rb +2 -2
  172. data/lib/rubocop/cop/style/numbered_parameters.rb +46 -0
  173. data/lib/rubocop/cop/style/numbered_parameters_limit.rb +50 -0
  174. data/lib/rubocop/cop/style/numeric_literals.rb +7 -8
  175. data/lib/rubocop/cop/style/numeric_predicate.rb +5 -0
  176. data/lib/rubocop/cop/style/open_struct_use.rb +69 -0
  177. data/lib/rubocop/cop/style/optional_arguments.rb +4 -0
  178. data/lib/rubocop/cop/style/optional_boolean_parameter.rb +14 -4
  179. data/lib/rubocop/cop/style/parallel_assignment.rb +1 -1
  180. data/lib/rubocop/cop/style/parentheses_around_condition.rb +12 -2
  181. data/lib/rubocop/cop/style/percent_q_literals.rb +2 -2
  182. data/lib/rubocop/cop/style/preferred_hash_methods.rb +9 -4
  183. data/lib/rubocop/cop/style/quoted_symbols.rb +21 -7
  184. data/lib/rubocop/cop/style/raise_args.rb +1 -1
  185. data/lib/rubocop/cop/style/redundant_argument.rb +19 -9
  186. data/lib/rubocop/cop/style/redundant_begin.rb +25 -0
  187. data/lib/rubocop/cop/style/redundant_condition.rb +2 -3
  188. data/lib/rubocop/cop/style/redundant_fetch_block.rb +4 -0
  189. data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +12 -3
  190. data/lib/rubocop/cop/style/redundant_freeze.rb +4 -4
  191. data/lib/rubocop/cop/style/redundant_interpolation.rb +1 -1
  192. data/lib/rubocop/cop/style/redundant_percent_q.rb +2 -3
  193. data/lib/rubocop/cop/style/redundant_self.rb +10 -0
  194. data/lib/rubocop/cop/style/redundant_self_assignment.rb +4 -3
  195. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +23 -28
  196. data/lib/rubocop/cop/style/redundant_sort.rb +51 -18
  197. data/lib/rubocop/cop/style/regexp_literal.rb +3 -3
  198. data/lib/rubocop/cop/style/return_nil.rb +2 -1
  199. data/lib/rubocop/cop/style/safe_navigation.rb +13 -2
  200. data/lib/rubocop/cop/style/select_by_regexp.rb +139 -0
  201. data/lib/rubocop/cop/style/single_argument_dig.rb +5 -0
  202. data/lib/rubocop/cop/style/slicing_with_range.rb +13 -0
  203. data/lib/rubocop/cop/style/sole_nested_conditional.rb +4 -0
  204. data/lib/rubocop/cop/style/special_global_vars.rb +4 -0
  205. data/lib/rubocop/cop/style/static_class.rb +5 -5
  206. data/lib/rubocop/cop/style/string_chars.rb +4 -2
  207. data/lib/rubocop/cop/style/string_concatenation.rb +5 -1
  208. data/lib/rubocop/cop/style/string_hash_keys.rb +4 -0
  209. data/lib/rubocop/cop/style/struct_inheritance.rb +4 -0
  210. data/lib/rubocop/cop/style/swap_values.rb +4 -2
  211. data/lib/rubocop/cop/style/symbol_array.rb +3 -3
  212. data/lib/rubocop/cop/style/symbol_proc.rb +26 -0
  213. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +19 -0
  214. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  215. data/lib/rubocop/cop/style/word_array.rb +3 -3
  216. data/lib/rubocop/cop/style/yoda_condition.rb +24 -7
  217. data/lib/rubocop/cop/style/zero_length_predicate.rb +6 -0
  218. data/lib/rubocop/cop/util.rb +15 -4
  219. data/lib/rubocop/cops_documentation_generator.rb +17 -5
  220. data/lib/rubocop/formatter/html_formatter.rb +5 -2
  221. data/lib/rubocop/formatter/json_formatter.rb +4 -1
  222. data/lib/rubocop/magic_comment.rb +44 -15
  223. data/lib/rubocop/options.rb +126 -112
  224. data/lib/rubocop/rake_task.rb +1 -1
  225. data/lib/rubocop/remote_config.rb +1 -1
  226. data/lib/rubocop/result_cache.rb +3 -3
  227. data/lib/rubocop/rspec/cop_helper.rb +1 -1
  228. data/lib/rubocop/rspec/expect_offense.rb +6 -2
  229. data/lib/rubocop/rspec/parallel_formatter.rb +90 -0
  230. data/lib/rubocop/rspec/support.rb +1 -0
  231. data/lib/rubocop/runner.rb +2 -3
  232. data/lib/rubocop/target_finder.rb +1 -1
  233. data/lib/rubocop/version.rb +1 -1
  234. data/lib/rubocop/yaml_duplication_checker.rb +1 -1
  235. data/lib/rubocop.rb +14 -2
  236. metadata +20 -5
@@ -14,8 +14,25 @@ module RuboCop
14
14
  # positives. Luckily, there is no harm in freezing an already
15
15
  # frozen object.
16
16
  #
17
+ # From Ruby 3.0, this cop honours the magic comment
18
+ # 'shareable_constant_value'. When this magic comment is set to any
19
+ # acceptable value other than none, it will suppress the offenses
20
+ # raised by this cop. It enforces frozen state.
21
+ #
17
22
  # NOTE: Regexp and Range literals are frozen objects since Ruby 3.0.
18
23
  #
24
+ # NOTE: From Ruby 3.0, interpolated strings are not frozen when
25
+ # `# frozen-string-literal: true` is used, so this cop enforces explicit
26
+ # freezing for such strings.
27
+ #
28
+ # NOTE: From Ruby 3.0, this cop allows explicit freezing of constants when
29
+ # the `shareable_constant_value` directive is used.
30
+ #
31
+ # @safety
32
+ # This cop's autocorrection is unsafe since any mutations on objects that
33
+ # are made frozen will change from being accepted to raising `FrozenError`,
34
+ # and will need to be manually refactored.
35
+ #
19
36
  # @example EnforcedStyle: literals (default)
20
37
  # # bad
21
38
  # CONST = [1, 2, 3]
@@ -52,7 +69,55 @@ module RuboCop
52
69
  # puts 1
53
70
  # end
54
71
  # end.freeze
72
+ #
73
+ # @example
74
+ # # Magic comment - shareable_constant_value: literal
75
+ #
76
+ # # bad
77
+ # CONST = [1, 2, 3]
78
+ #
79
+ # # good
80
+ # # shareable_constant_value: literal
81
+ # CONST = [1, 2, 3]
82
+ #
55
83
  class MutableConstant < Base
84
+ # Handles magic comment shareable_constant_value with O(n ^ 2) complexity
85
+ # n - number of lines in the source
86
+ # Iterates over all lines before a CONSTANT
87
+ # until it reaches shareable_constant_value
88
+ module ShareableConstantValue
89
+ module_function
90
+
91
+ def recent_shareable_value?(node)
92
+ shareable_constant_comment = magic_comment_in_scope node
93
+ return false if shareable_constant_comment.nil?
94
+
95
+ shareable_constant_value = MagicComment.parse(shareable_constant_comment)
96
+ .shareable_constant_value
97
+ shareable_constant_value_enabled? shareable_constant_value
98
+ end
99
+
100
+ # Identifies the most recent magic comment with valid shareable constant values
101
+ # that's in scope for this node
102
+ def magic_comment_in_scope(node)
103
+ processed_source_till_node(node).reverse_each.find do |line|
104
+ MagicComment.parse(line).valid_shareable_constant_value?
105
+ end
106
+ end
107
+
108
+ private
109
+
110
+ def processed_source_till_node(node)
111
+ processed_source.lines[0..(node.last_line - 1)]
112
+ end
113
+
114
+ def shareable_constant_value_enabled?(value)
115
+ %w[literal experimental_everything experimental_copy].include? value
116
+ end
117
+ end
118
+ private_constant :ShareableConstantValue
119
+
120
+ include ShareableConstantValue
56
121
  include FrozenStringLiteral
57
122
  include ConfigurableEnforcedStyle
58
123
  extend AutoCorrector
@@ -85,18 +150,18 @@ module RuboCop
85
150
  return if immutable_literal?(value)
86
151
  return if operation_produces_immutable_object?(value)
87
152
  return if frozen_string_literal?(value)
153
+ return if shareable_constant_value?(value)
88
154
 
89
155
  add_offense(value) { |corrector| autocorrect(corrector, value) }
90
156
  end
91
157
 
92
158
  def check(value)
93
159
  range_enclosed_in_parentheses = range_enclosed_in_parentheses?(value)
94
-
95
160
  return unless mutable_literal?(value) ||
96
- target_ruby_version <= 2.7 && range_enclosed_in_parentheses
161
+ (target_ruby_version <= 2.7 && range_enclosed_in_parentheses)
97
162
 
98
- return if FROZEN_STRING_LITERAL_TYPES.include?(value.type) &&
99
- frozen_string_literals_enabled?
163
+ return if frozen_string_literal?(value)
164
+ return if shareable_constant_value?(value)
100
165
 
101
166
  add_offense(value) { |corrector| autocorrect(corrector, value) }
102
167
  end
@@ -126,8 +191,10 @@ module RuboCop
126
191
  frozen_regexp_or_range_literals?(node) || node.immutable_literal?
127
192
  end
128
193
 
129
- def frozen_string_literal?(node)
130
- FROZEN_STRING_LITERAL_TYPES.include?(node.type) && frozen_string_literals_enabled?
194
+ def shareable_constant_value?(node)
195
+ return false if target_ruby_version < 3.0
196
+
197
+ recent_shareable_value? node
131
198
  end
132
199
 
133
200
  def frozen_regexp_or_range_literals?(node)
@@ -90,7 +90,7 @@ module RuboCop
90
90
  end
91
91
 
92
92
  def correct_style?(node)
93
- style == :prefix && node.modifier_form? || style == :postfix && !node.modifier_form?
93
+ (style == :prefix && node.modifier_form?) || (style == :postfix && !node.modifier_form?)
94
94
  end
95
95
  end
96
96
  end
@@ -80,7 +80,7 @@ module RuboCop
80
80
  end
81
81
 
82
82
  def correct_style?(node)
83
- style == :prefix && node.modifier_form? || style == :postfix && !node.modifier_form?
83
+ (style == :prefix && node.modifier_form?) || (style == :postfix && !node.modifier_form?)
84
84
  end
85
85
  end
86
86
  end
@@ -63,7 +63,7 @@ module RuboCop
63
63
 
64
64
  def on_send(node)
65
65
  return if ignored_node?(node) ||
66
- !include_semantic_changes? && nil_comparison_style == 'comparison'
66
+ (!include_semantic_changes? && nil_comparison_style == 'comparison')
67
67
  return unless register_offense?(node)
68
68
 
69
69
  message = message(node)
@@ -87,7 +87,7 @@ module RuboCop
87
87
 
88
88
  def register_offense?(node)
89
89
  not_equal_to_nil?(node) ||
90
- include_semantic_changes? && (not_and_nil_check?(node) || unless_and_nil_check?(node))
90
+ (include_semantic_changes? && (not_and_nil_check?(node) || unless_and_nil_check?(node)))
91
91
  end
92
92
 
93
93
  def autocorrect(corrector, node)
@@ -53,8 +53,8 @@ module RuboCop
53
53
 
54
54
  def requires_parens?(child)
55
55
  child.and_type? || child.or_type? ||
56
- child.send_type? && child.binary_operation? ||
57
- child.if_type? && child.ternary?
56
+ (child.send_type? && child.binary_operation?) ||
57
+ (child.if_type? && child.ternary?)
58
58
  end
59
59
 
60
60
  def correct_opposite_method(corrector, range, child)
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop checks for numbered parameters.
7
+ #
8
+ # It can either restrict the use of numbered parameters to
9
+ # single-lined blocks, or disallow completely numbered parameters.
10
+ #
11
+ # @example EnforcedStyle: allow_single_line (default)
12
+ # # bad
13
+ # collection.each do
14
+ # puts _1
15
+ # end
16
+ #
17
+ # # good
18
+ # collection.each { puts _1 }
19
+ #
20
+ # @example EnforcedStyle: disallow
21
+ # # bad
22
+ # collection.each { puts _1 }
23
+ #
24
+ # # good
25
+ # collection.each { |item| puts item }
26
+ #
27
+ class NumberedParameters < Base
28
+ include ConfigurableEnforcedStyle
29
+ extend TargetRubyVersion
30
+
31
+ MSG_DISALLOW = 'Avoid using numbered parameters.'
32
+ MSG_MULTI_LINE = 'Avoid using numbered parameters for multi-line blocks.'
33
+
34
+ minimum_target_ruby_version 2.7
35
+
36
+ def on_numblock(node)
37
+ if style == :disallow
38
+ add_offense(node, message: MSG_DISALLOW)
39
+ elsif node.multiline?
40
+ add_offense(node, message: MSG_MULTI_LINE)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop detects use of an excessive amount of numbered parameters in a
7
+ # single block. Having too many numbered parameters can make code too
8
+ # cryptic and hard to read.
9
+ #
10
+ # The cop defaults to registering an offense if there is more than 1 numbered
11
+ # parameter but this maximum can be configured by setting `Max`.
12
+ #
13
+ # @example Max: 1 (default)
14
+ # # bad
15
+ # foo { _1.call(_2, _3, _4) }
16
+ #
17
+ # # good
18
+ # foo { do_something(_1) }
19
+ class NumberedParametersLimit < Base
20
+ extend TargetRubyVersion
21
+ extend ExcludeLimit
22
+
23
+ DEFAULT_MAX_VALUE = 1
24
+
25
+ minimum_target_ruby_version 2.7
26
+ exclude_limit 'Max'
27
+
28
+ MSG = 'Avoid using more than %<max>i numbered %<parameter>s; %<count>i detected.'
29
+
30
+ def on_numblock(node)
31
+ _send_node, param_count, * = *node
32
+ return if param_count <= max_count
33
+
34
+ parameter = max_count > 1 ? 'parameters' : 'parameter'
35
+ message = format(MSG, max: max_count, parameter: parameter, count: param_count)
36
+ add_offense(node, message: message) { self.max = param_count }
37
+ end
38
+
39
+ private
40
+
41
+ def max_count
42
+ max = cop_config.fetch('Max', DEFAULT_MAX_VALUE)
43
+
44
+ # Ruby does not allow more than 9 numbered parameters
45
+ [max, 9].min
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -58,18 +58,17 @@ module RuboCop
58
58
 
59
59
  case int
60
60
  when /^\d+$/
61
- return unless (self.min_digits = int.size + 1)
62
-
63
- register_offense(node)
61
+ register_offense(node) { self.min_digits = int.size + 1 }
64
62
  when /\d{4}/, short_group_regex
65
- return unless (self.config_to_allow_offenses = { 'Enabled' => false })
66
-
67
- register_offense(node)
63
+ register_offense(node) { self.config_to_allow_offenses = { 'Enabled' => false } }
68
64
  end
69
65
  end
70
66
 
71
- def register_offense(node)
72
- add_offense(node) { |corrector| corrector.replace(node, format_number(node)) }
67
+ def register_offense(node, &_block)
68
+ add_offense(node) do |corrector|
69
+ yield
70
+ corrector.replace(node, format_number(node))
71
+ end
73
72
  end
74
73
 
75
74
  def short_group_regex
@@ -16,6 +16,11 @@ module RuboCop
16
16
  # populated with objects which can be compared with integers, but are
17
17
  # not themselves `Integer` polymorphic.
18
18
  #
19
+ # @safety
20
+ # This cop is unsafe because it cannot be guaranteed that the receiver
21
+ # defines the predicates or can be compared to a number, which may lead
22
+ # to a false positive for non-standard classes.
23
+ #
19
24
  # @example EnforcedStyle: predicate (default)
20
25
  # # bad
21
26
  #
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop flags uses of OpenStruct, as it is now officially discouraged
7
+ # to be used for performance, version compatibility, and potential security issues.
8
+ #
9
+ # @safety
10
+ #
11
+ # Note that this cop may flag false positives; for instance, the following legal
12
+ # use of a hand-rolled `OpenStruct` type would be considered an offense:
13
+ #
14
+ # ```
15
+ # module MyNamespace
16
+ # class OpenStruct # not the OpenStruct we're looking for
17
+ # end
18
+ #
19
+ # def new_struct
20
+ # OpenStruct.new # resolves to MyNamespace::OpenStruct
21
+ # end
22
+ # end
23
+ # ```
24
+ #
25
+ # @example
26
+ #
27
+ # # bad
28
+ # point = OpenStruct.new(x: 0, y: 1)
29
+ #
30
+ # # good
31
+ # Point = Struct.new(:x, :y)
32
+ # point = Point.new(0, 1)
33
+ #
34
+ # # also good
35
+ # point = { x: 0, y: 1 }
36
+ #
37
+ # # bad
38
+ # test_double = OpenStruct.new(a: 'b')
39
+ #
40
+ # # good (assumes test using rspec-mocks)
41
+ # test_double = double
42
+ # allow(test_double).to receive(:a).and_return('b')
43
+ #
44
+ class OpenStructUse < Base
45
+ MSG = 'Avoid using `OpenStruct`; use `Struct`, `Hash`, a class or test doubles instead.'
46
+
47
+ # @!method uses_open_struct?(node)
48
+ def_node_matcher :uses_open_struct?, <<-PATTERN
49
+ (const {nil? (cbase)} :OpenStruct)
50
+ PATTERN
51
+
52
+ def on_const(node)
53
+ return unless uses_open_struct?(node)
54
+ return if custom_class_or_module_definition?(node)
55
+
56
+ add_offense(node)
57
+ end
58
+
59
+ private
60
+
61
+ def custom_class_or_module_definition?(node)
62
+ parent = node.parent
63
+
64
+ (parent.class_type? || parent.module_type?) && node.left_siblings.empty?
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -6,6 +6,10 @@ module RuboCop
6
6
  # This cop checks for optional arguments to methods
7
7
  # that do not come at the end of the argument list.
8
8
  #
9
+ # @safety
10
+ # This cop is unsafe because changing a method signature will
11
+ # implicitly change behaviour.
12
+ #
9
13
  # @example
10
14
  # # bad
11
15
  # def foo(a = 1, b, c)
@@ -7,6 +7,10 @@ module RuboCop
7
7
  # boolean arguments when defining methods. `respond_to_missing?` method is allowed by default.
8
8
  # These are customizable with `AllowedMethods` option.
9
9
  #
10
+ # @safety
11
+ # This cop is unsafe because changing a method signature will
12
+ # implicitly change behaviour.
13
+ #
10
14
  # @example
11
15
  # # bad
12
16
  # def some_method(bar = false)
@@ -33,8 +37,8 @@ module RuboCop
33
37
  class OptionalBooleanParameter < Base
34
38
  include AllowedMethods
35
39
 
36
- MSG = 'Use keyword arguments when defining method with boolean argument.'
37
- BOOLEAN_TYPES = %i[true false].freeze
40
+ MSG = 'Prefer keyword arguments for arguments with a boolean default value; ' \
41
+ 'use `%<replacement>s` instead of `%<original>s`.'
38
42
 
39
43
  def on_def(node)
40
44
  return if allowed_method?(node.method_name)
@@ -42,11 +46,17 @@ module RuboCop
42
46
  node.arguments.each do |arg|
43
47
  next unless arg.optarg_type?
44
48
 
45
- _name, value = *arg
46
- add_offense(arg) if BOOLEAN_TYPES.include?(value.type)
49
+ add_offense(arg, message: format_message(arg)) if arg.default_value.boolean_type?
47
50
  end
48
51
  end
49
52
  alias on_defs on_def
53
+
54
+ private
55
+
56
+ def format_message(argument)
57
+ source = argument.source
58
+ format(MSG, original: source, replacement: source.sub(/\s+=/, ':'))
59
+ end
50
60
  end
51
61
  end
52
62
  end
@@ -153,7 +153,7 @@ module RuboCop
153
153
 
154
154
  def dependency?(lhs, rhs)
155
155
  uses_var?(rhs, var_name(lhs)) ||
156
- lhs.send_type? && lhs.assignment_method? && accesses?(rhs, lhs)
156
+ (lhs.send_type? && lhs.assignment_method? && accesses?(rhs, lhs))
157
157
  end
158
158
 
159
159
  # `lhs` is an assignment method call like `obj.attr=` or `ary[idx]=`.
@@ -56,6 +56,7 @@ module RuboCop
56
56
  class ParenthesesAroundCondition < Base
57
57
  include SafeAssignment
58
58
  include Parentheses
59
+ include RangeHelp
59
60
  extend AutoCorrector
60
61
 
61
62
  def on_if(node)
@@ -73,13 +74,14 @@ module RuboCop
73
74
 
74
75
  # @!method control_op_condition(node)
75
76
  def_node_matcher :control_op_condition, <<~PATTERN
76
- (begin $_ ...)
77
+ (begin $_ $...)
77
78
  PATTERN
78
79
 
79
80
  def process_control_op(node)
80
81
  cond = node.condition
81
82
 
82
- control_op_condition(cond) do |first_child|
83
+ control_op_condition(cond) do |first_child, rest_children|
84
+ return if semicolon_separated_expressions?(first_child, rest_children)
83
85
  return if modifier_op?(first_child)
84
86
  return if parens_allowed?(cond)
85
87
 
@@ -90,6 +92,14 @@ module RuboCop
90
92
  end
91
93
  end
92
94
 
95
+ def semicolon_separated_expressions?(first_exp, rest_exps)
96
+ return false unless (second_exp = rest_exps.first)
97
+
98
+ range = range_between(first_exp.source_range.end_pos, second_exp.source_range.begin_pos)
99
+
100
+ range.source.include?(';')
101
+ end
102
+
93
103
  def modifier_op?(node)
94
104
  return false if node.if_type? && node.ternary?
95
105
  return true if node.rescue_type?
@@ -53,8 +53,8 @@ module RuboCop
53
53
  end
54
54
 
55
55
  def correct_literal_style?(node)
56
- style == :lower_case_q && type(node) == '%q' ||
57
- style == :upper_case_q && type(node) == '%Q'
56
+ (style == :lower_case_q && type(node) == '%q') ||
57
+ (style == :upper_case_q && type(node) == '%Q')
58
58
  end
59
59
 
60
60
  def message(_range)
@@ -3,10 +3,15 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # This cop (by default) checks for uses of methods Hash#has_key? and
7
- # Hash#has_value? where it enforces Hash#key? and Hash#value?
8
- # It is configurable to enforce the inverse, using `verbose` method
9
- # names also.
6
+ # This cop checks for uses of methods `Hash#has_key?` and
7
+ # `Hash#has_value?`, and suggests using `Hash#key?` and `Hash#value?` instead.
8
+ #
9
+ # It is configurable to enforce the verbose method names, by using the
10
+ # `EnforcedStyle: verbose` configuration.
11
+ #
12
+ # @safety
13
+ # This cop is unsafe because it cannot be guaranteed that the receiver
14
+ # is a `Hash` or responds to the replacement methods.
10
15
  #
11
16
  # @example EnforcedStyle: short (default)
12
17
  # # bad
@@ -4,7 +4,8 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # Checks if the quotes used for quoted symbols match the configured defaults.
7
- # By default uses the same configuration as `Style/StringLiterals`.
7
+ # By default uses the same configuration as `Style/StringLiterals`; if that
8
+ # cop is not enabled, the default `EnforcedStyle` is `single_quotes`.
8
9
  #
9
10
  # String interpolation is always kept in double quotes.
10
11
  #
@@ -45,7 +46,7 @@ module RuboCop
45
46
 
46
47
  message = style == :single_quotes ? MSG_SINGLE : MSG_DOUBLE
47
48
 
48
- if wrong_quotes?(node)
49
+ if wrong_quotes?(node) || invalid_double_quotes?(node.source)
49
50
  add_offense(node, message: message) do |corrector|
50
51
  opposite_style_detected
51
52
  autocorrect(corrector, node)
@@ -57,6 +58,16 @@ module RuboCop
57
58
 
58
59
  private
59
60
 
61
+ def invalid_double_quotes?(source)
62
+ return false unless style == :double_quotes
63
+
64
+ # The string needs single quotes if:
65
+ # 1. It contains a double quote
66
+ # 2. It contains text that would become an escape sequence with double quotes
67
+ # 3. It contains text that would become an interpolation with double quotes
68
+ !/" | (?<!\\)\\[aAbcdefkMnprsStuUxzZ0-7] | \#[@{$]/x.match?(source)
69
+ end
70
+
60
71
  def autocorrect(corrector, node)
61
72
  str = if hash_colon_key?(node)
62
73
  # strip quotes
@@ -75,11 +86,14 @@ module RuboCop
75
86
  end
76
87
 
77
88
  def correct_quotes(str)
78
- if style == :single_quotes
79
- to_string_literal(str)
80
- else
81
- str.inspect
82
- end
89
+ correction = if style == :single_quotes
90
+ to_string_literal(str)
91
+ else
92
+ str.gsub("\\'", "'").inspect
93
+ end
94
+
95
+ # The conversion process doubles escaped slashes, so they have to be reverted
96
+ correction.gsub('\\\\', '\\')
83
97
  end
84
98
 
85
99
  def style
@@ -141,7 +141,7 @@ module RuboCop
141
141
  end
142
142
 
143
143
  def requires_parens?(parent)
144
- parent.and_type? || parent.or_type? || parent.if_type? && parent.ternary?
144
+ parent.and_type? || parent.or_type? || (parent.if_type? && parent.ternary?)
145
145
  end
146
146
  end
147
147
  end
@@ -5,22 +5,29 @@ module RuboCop
5
5
  module Style
6
6
  # This cop checks for a redundant argument passed to certain methods.
7
7
  #
8
- # Limitations:
9
- #
10
- # 1. This cop matches for method names only and hence cannot tell apart
11
- # methods with same name in different classes.
12
- # 2. This cop is limited to methods with single parameter.
13
- # 3. This cop is unsafe if certain special global variables (e.g. `$;`, `$/`) are set.
14
- # That depends on the nature of the target methods, of course.
8
+ # NOTE: This cop is limited to methods with single parameter.
15
9
  #
16
10
  # Method names and their redundant arguments can be configured like this:
17
11
  #
12
+ # [source,yaml]
13
+ # ----
18
14
  # Methods:
19
15
  # join: ''
20
16
  # split: ' '
21
17
  # chomp: "\n"
22
18
  # chomp!: "\n"
23
19
  # foo: 2
20
+ # ----
21
+ #
22
+ # @safety
23
+ # This cop is unsafe because of the following limitations:
24
+ #
25
+ # 1. This cop matches by method names only and hence cannot tell apart
26
+ # methods with same name in different classes.
27
+ # 2. This cop may be unsafe if certain special global variables (e.g. `$;`, `$/`) are set.
28
+ # That depends on the nature of the target methods, of course. For example, the default
29
+ # argument to join is `$OUTPUT_FIELD_SEPARATOR` (or `$,`) rather than `''`, and if that
30
+ # global is changed, `''` is no longer a redundant argument.
24
31
  #
25
32
  # @example
26
33
  # # bad
@@ -51,8 +58,11 @@ module RuboCop
51
58
  return if node.arguments.count != 1
52
59
  return unless redundant_argument?(node)
53
60
 
54
- add_offense(node, message: format(MSG, arg: node.arguments.first.source)) do |corrector|
55
- corrector.remove(argument_range(node))
61
+ offense_range = argument_range(node)
62
+ message = format(MSG, arg: node.arguments.first.source)
63
+
64
+ add_offense(offense_range, message: message) do |corrector|
65
+ corrector.remove(offense_range)
56
66
  end
57
67
  end
58
68
 
@@ -84,6 +84,7 @@ module RuboCop
84
84
 
85
85
  def on_kwbegin(node)
86
86
  return if empty_begin?(node) ||
87
+ begin_block_has_multiline_statements?(node) ||
87
88
  contain_rescue_or_ensure?(node) ||
88
89
  valid_context_using_only_begin?(node)
89
90
 
@@ -102,6 +103,9 @@ module RuboCop
102
103
  corrector.remove(offense_range)
103
104
  end
104
105
 
106
+ if use_modifier_form_after_multiline_begin_block?(node)
107
+ correct_modifier_form_after_multiline_begin_block(corrector, node)
108
+ end
105
109
  corrector.remove(node.loc.end)
106
110
  end
107
111
  end
@@ -127,10 +131,31 @@ module RuboCop
127
131
  corrector.insert_before(node.parent, comments) unless comments.blank?
128
132
  end
129
133
 
134
+ def use_modifier_form_after_multiline_begin_block?(node)
135
+ return unless (parent = node.parent)
136
+
137
+ node.multiline? && parent.if_type? && parent.modifier_form?
138
+ end
139
+
140
+ def correct_modifier_form_after_multiline_begin_block(corrector, node)
141
+ condition_range = condition_range(node.parent)
142
+
143
+ corrector.insert_after(node.children.first, " #{condition_range.source}")
144
+ corrector.remove(range_by_whole_lines(condition_range, include_final_newline: true))
145
+ end
146
+
147
+ def condition_range(node)
148
+ range_between(node.loc.keyword.begin_pos, node.condition.source_range.end_pos)
149
+ end
150
+
130
151
  def empty_begin?(node)
131
152
  node.children.empty?
132
153
  end
133
154
 
155
+ def begin_block_has_multiline_statements?(node)
156
+ node.children.count >= 2
157
+ end
158
+
134
159
  def contain_rescue_or_ensure?(node)
135
160
  first_child = node.children.first
136
161