rubocop 1.18.1 → 1.22.3

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 (249) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +116 -23
  4. data/lib/rubocop/cli.rb +18 -0
  5. data/lib/rubocop/config.rb +5 -0
  6. data/lib/rubocop/config_loader.rb +5 -3
  7. data/lib/rubocop/config_loader_resolver.rb +22 -7
  8. data/lib/rubocop/config_validator.rb +27 -6
  9. data/lib/rubocop/cop/base.rb +3 -3
  10. data/lib/rubocop/cop/bundler/gem_comment.rb +3 -3
  11. data/lib/rubocop/cop/bundler/gem_filename.rb +103 -0
  12. data/lib/rubocop/cop/bundler/insecure_protocol_source.rb +45 -21
  13. data/lib/rubocop/cop/bundler/ordered_gems.rb +3 -12
  14. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +2 -2
  15. data/lib/rubocop/cop/correctors/line_break_corrector.rb +1 -1
  16. data/lib/rubocop/cop/correctors/ordered_gem_corrector.rb +11 -10
  17. data/lib/rubocop/cop/correctors/require_library_corrector.rb +23 -0
  18. data/lib/rubocop/cop/documentation.rb +1 -1
  19. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +3 -12
  20. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +31 -24
  21. data/lib/rubocop/cop/generator.rb +14 -8
  22. data/lib/rubocop/cop/internal_affairs/inherit_deprecated_cop_class.rb +34 -0
  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/undefined_config.rb +71 -0
  26. data/lib/rubocop/cop/internal_affairs.rb +3 -0
  27. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
  28. data/lib/rubocop/cop/layout/assignment_indentation.rb +1 -1
  29. data/lib/rubocop/cop/layout/block_alignment.rb +3 -3
  30. data/lib/rubocop/cop/layout/class_structure.rb +7 -2
  31. data/lib/rubocop/cop/layout/dot_position.rb +34 -5
  32. data/lib/rubocop/cop/layout/empty_comment.rb +1 -1
  33. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +9 -0
  34. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +7 -4
  35. data/lib/rubocop/cop/layout/end_alignment.rb +9 -2
  36. data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -1
  37. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +1 -1
  38. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +1 -1
  39. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +1 -1
  40. data/lib/rubocop/cop/layout/hash_alignment.rb +22 -18
  41. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +1 -1
  42. data/lib/rubocop/cop/layout/heredoc_indentation.rb +0 -7
  43. data/lib/rubocop/cop/layout/indentation_style.rb +2 -2
  44. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  45. data/lib/rubocop/cop/layout/leading_comment_space.rb +2 -2
  46. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +36 -17
  47. data/lib/rubocop/cop/layout/line_length.rb +9 -7
  48. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +1 -1
  49. data/lib/rubocop/cop/layout/multiline_block_layout.rb +3 -3
  50. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +3 -0
  51. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +1 -1
  52. data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -0
  53. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +23 -10
  54. data/lib/rubocop/cop/layout/single_line_block_chain.rb +15 -4
  55. data/lib/rubocop/cop/layout/space_after_not.rb +1 -0
  56. data/lib/rubocop/cop/layout/space_around_equals_in_parameter_default.rb +2 -1
  57. data/lib/rubocop/cop/layout/space_around_keyword.rb +2 -2
  58. data/lib/rubocop/cop/layout/space_around_operators.rb +12 -1
  59. data/lib/rubocop/cop/layout/space_before_brackets.rb +1 -0
  60. data/lib/rubocop/cop/layout/space_before_comment.rb +2 -2
  61. data/lib/rubocop/cop/layout/space_inside_parens.rb +78 -32
  62. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +1 -1
  63. data/lib/rubocop/cop/layout/trailing_whitespace.rb +24 -1
  64. data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +111 -0
  65. data/lib/rubocop/cop/lint/ambiguous_range.rb +105 -0
  66. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +5 -2
  67. data/lib/rubocop/cop/lint/assignment_in_condition.rb +7 -5
  68. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +18 -5
  69. data/lib/rubocop/cop/lint/boolean_symbol.rb +5 -0
  70. data/lib/rubocop/cop/lint/debugger.rb +2 -4
  71. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +4 -4
  72. data/lib/rubocop/cop/lint/deprecated_constants.rb +3 -2
  73. data/lib/rubocop/cop/lint/disjunctive_assignment_in_constructor.rb +24 -1
  74. data/lib/rubocop/cop/lint/duplicate_branch.rb +2 -1
  75. data/lib/rubocop/cop/lint/duplicate_methods.rb +8 -5
  76. data/lib/rubocop/cop/lint/else_layout.rb +10 -6
  77. data/lib/rubocop/cop/lint/empty_in_pattern.rb +1 -1
  78. data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
  79. data/lib/rubocop/cop/lint/float_out_of_range.rb +1 -1
  80. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +12 -3
  81. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +67 -0
  82. data/lib/rubocop/cop/lint/interpolation_check.rb +5 -0
  83. data/lib/rubocop/cop/lint/loop.rb +4 -3
  84. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +5 -1
  85. data/lib/rubocop/cop/lint/number_conversion.rb +12 -1
  86. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -1
  87. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +4 -2
  88. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +17 -0
  89. data/lib/rubocop/cop/lint/percent_string_array.rb +10 -0
  90. data/lib/rubocop/cop/lint/raise_exception.rb +4 -0
  91. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +5 -4
  92. data/lib/rubocop/cop/lint/require_relative_self_path.rb +50 -0
  93. data/lib/rubocop/cop/lint/shadowed_argument.rb +1 -1
  94. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +1 -1
  95. data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
  96. data/lib/rubocop/cop/lint/triple_quotes.rb +1 -1
  97. data/lib/rubocop/cop/lint/unexpected_block_arity.rb +8 -3
  98. data/lib/rubocop/cop/lint/unused_method_argument.rb +2 -3
  99. data/lib/rubocop/cop/lint/useless_method_definition.rb +3 -2
  100. data/lib/rubocop/cop/lint/useless_setter_call.rb +7 -4
  101. data/lib/rubocop/cop/lint/useless_times.rb +5 -4
  102. data/lib/rubocop/cop/metrics/abc_size.rb +6 -0
  103. data/lib/rubocop/cop/metrics/parameter_lists.rb +5 -2
  104. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
  105. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +1 -1
  106. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  107. data/lib/rubocop/cop/mixin/annotation_comment.rb +57 -34
  108. data/lib/rubocop/cop/mixin/check_line_breakable.rb +2 -2
  109. data/lib/rubocop/cop/mixin/code_length.rb +1 -1
  110. data/lib/rubocop/cop/mixin/documentation_comment.rb +5 -2
  111. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -2
  112. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +23 -1
  113. data/lib/rubocop/cop/mixin/hash_transform_method.rb +9 -4
  114. data/lib/rubocop/cop/mixin/heredoc.rb +5 -0
  115. data/lib/rubocop/cop/mixin/multiline_element_indentation.rb +1 -1
  116. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +2 -2
  117. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +1 -1
  118. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +9 -1
  119. data/lib/rubocop/cop/mixin/percent_array.rb +18 -7
  120. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +9 -1
  121. data/lib/rubocop/cop/mixin/require_library.rb +59 -0
  122. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +1 -1
  123. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +2 -2
  124. data/lib/rubocop/cop/mixin/statement_modifier.rb +1 -1
  125. data/lib/rubocop/cop/mixin/string_literals_help.rb +5 -1
  126. data/lib/rubocop/cop/mixin/trailing_body.rb +1 -1
  127. data/lib/rubocop/cop/naming/ascii_identifiers.rb +0 -3
  128. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  129. data/lib/rubocop/cop/naming/constant_name.rb +1 -1
  130. data/lib/rubocop/cop/naming/inclusive_language.rb +27 -10
  131. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +5 -4
  132. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +7 -0
  133. data/lib/rubocop/cop/security/io_methods.rb +49 -0
  134. data/lib/rubocop/cop/security/json_load.rb +8 -7
  135. data/lib/rubocop/cop/security/open.rb +4 -0
  136. data/lib/rubocop/cop/security/yaml_load.rb +4 -0
  137. data/lib/rubocop/cop/style/accessor_grouping.rb +2 -2
  138. data/lib/rubocop/cop/style/and_or.rb +5 -0
  139. data/lib/rubocop/cop/style/arguments_forwarding.rb +13 -2
  140. data/lib/rubocop/cop/style/array_coercion.rb +21 -3
  141. data/lib/rubocop/cop/style/ascii_comments.rb +0 -3
  142. data/lib/rubocop/cop/style/block_delimiters.rb +50 -2
  143. data/lib/rubocop/cop/style/case_equality.rb +6 -9
  144. data/lib/rubocop/cop/style/case_like_if.rb +5 -0
  145. data/lib/rubocop/cop/style/class_and_module_children.rb +9 -0
  146. data/lib/rubocop/cop/style/collection_compact.rb +7 -5
  147. data/lib/rubocop/cop/style/collection_methods.rb +8 -6
  148. data/lib/rubocop/cop/style/combinable_loops.rb +3 -2
  149. data/lib/rubocop/cop/style/comment_annotation.rb +55 -25
  150. data/lib/rubocop/cop/style/commented_keyword.rb +9 -3
  151. data/lib/rubocop/cop/style/conditional_assignment.rb +19 -5
  152. data/lib/rubocop/cop/style/date_time.rb +5 -0
  153. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +1 -1
  154. data/lib/rubocop/cop/style/documentation.rb +23 -8
  155. data/lib/rubocop/cop/style/double_cop_disable_directive.rb +1 -7
  156. data/lib/rubocop/cop/style/double_negation.rb +27 -6
  157. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  158. data/lib/rubocop/cop/style/encoding.rb +26 -15
  159. data/lib/rubocop/cop/style/eval_with_location.rb +1 -1
  160. data/lib/rubocop/cop/style/explicit_block_argument.rb +46 -11
  161. data/lib/rubocop/cop/style/float_division.rb +10 -2
  162. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +14 -3
  163. data/lib/rubocop/cop/style/global_std_stream.rb +4 -0
  164. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +11 -0
  165. data/lib/rubocop/cop/style/hash_each_methods.rb +5 -0
  166. data/lib/rubocop/cop/style/hash_except.rb +4 -3
  167. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  168. data/lib/rubocop/cop/style/hash_transform_keys.rb +4 -9
  169. data/lib/rubocop/cop/style/hash_transform_values.rb +4 -6
  170. data/lib/rubocop/cop/style/identical_conditional_branches.rb +32 -5
  171. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +18 -4
  172. data/lib/rubocop/cop/style/infinite_loop.rb +4 -3
  173. data/lib/rubocop/cop/style/inverse_methods.rb +9 -2
  174. data/lib/rubocop/cop/style/lambda_call.rb +1 -1
  175. data/lib/rubocop/cop/style/line_end_concatenation.rb +14 -1
  176. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +6 -6
  177. data/lib/rubocop/cop/style/method_def_parentheses.rb +10 -1
  178. data/lib/rubocop/cop/style/missing_else.rb +7 -0
  179. data/lib/rubocop/cop/style/module_function.rb +8 -9
  180. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +1 -1
  181. data/lib/rubocop/cop/style/multiline_when_then.rb +1 -1
  182. data/lib/rubocop/cop/style/mutable_constant.rb +79 -14
  183. data/lib/rubocop/cop/style/negated_if.rb +1 -1
  184. data/lib/rubocop/cop/style/negated_unless.rb +1 -1
  185. data/lib/rubocop/cop/style/non_nil_check.rb +2 -2
  186. data/lib/rubocop/cop/style/not.rb +2 -2
  187. data/lib/rubocop/cop/style/numbered_parameters.rb +46 -0
  188. data/lib/rubocop/cop/style/numbered_parameters_limit.rb +50 -0
  189. data/lib/rubocop/cop/style/numeric_literals.rb +7 -8
  190. data/lib/rubocop/cop/style/numeric_predicate.rb +5 -0
  191. data/lib/rubocop/cop/style/optional_arguments.rb +4 -0
  192. data/lib/rubocop/cop/style/optional_boolean_parameter.rb +14 -4
  193. data/lib/rubocop/cop/style/parallel_assignment.rb +1 -1
  194. data/lib/rubocop/cop/style/percent_q_literals.rb +2 -2
  195. data/lib/rubocop/cop/style/preferred_hash_methods.rb +9 -4
  196. data/lib/rubocop/cop/style/quoted_symbols.rb +10 -6
  197. data/lib/rubocop/cop/style/raise_args.rb +1 -1
  198. data/lib/rubocop/cop/style/redundant_argument.rb +19 -9
  199. data/lib/rubocop/cop/style/redundant_begin.rb +25 -0
  200. data/lib/rubocop/cop/style/redundant_condition.rb +2 -3
  201. data/lib/rubocop/cop/style/redundant_fetch_block.rb +4 -0
  202. data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +12 -3
  203. data/lib/rubocop/cop/style/redundant_freeze.rb +4 -4
  204. data/lib/rubocop/cop/style/redundant_interpolation.rb +1 -1
  205. data/lib/rubocop/cop/style/redundant_percent_q.rb +2 -3
  206. data/lib/rubocop/cop/style/redundant_self.rb +10 -0
  207. data/lib/rubocop/cop/style/redundant_self_assignment.rb +4 -3
  208. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +83 -0
  209. data/lib/rubocop/cop/style/redundant_sort.rb +53 -20
  210. data/lib/rubocop/cop/style/regexp_literal.rb +3 -3
  211. data/lib/rubocop/cop/style/return_nil.rb +2 -1
  212. data/lib/rubocop/cop/style/safe_navigation.rb +13 -2
  213. data/lib/rubocop/cop/style/select_by_regexp.rb +139 -0
  214. data/lib/rubocop/cop/style/semicolon.rb +32 -24
  215. data/lib/rubocop/cop/style/single_argument_dig.rb +5 -0
  216. data/lib/rubocop/cop/style/single_line_block_params.rb +3 -1
  217. data/lib/rubocop/cop/style/single_line_methods.rb +25 -15
  218. data/lib/rubocop/cop/style/slicing_with_range.rb +13 -0
  219. data/lib/rubocop/cop/style/sole_nested_conditional.rb +4 -0
  220. data/lib/rubocop/cop/style/special_global_vars.rb +25 -0
  221. data/lib/rubocop/cop/style/static_class.rb +5 -5
  222. data/lib/rubocop/cop/style/string_chars.rb +4 -2
  223. data/lib/rubocop/cop/style/string_concatenation.rb +5 -1
  224. data/lib/rubocop/cop/style/string_hash_keys.rb +4 -0
  225. data/lib/rubocop/cop/style/struct_inheritance.rb +4 -0
  226. data/lib/rubocop/cop/style/swap_values.rb +4 -2
  227. data/lib/rubocop/cop/style/symbol_array.rb +3 -3
  228. data/lib/rubocop/cop/style/symbol_proc.rb +26 -0
  229. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +19 -0
  230. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  231. data/lib/rubocop/cop/style/word_array.rb +23 -5
  232. data/lib/rubocop/cop/style/yoda_condition.rb +24 -7
  233. data/lib/rubocop/cop/style/zero_length_predicate.rb +6 -0
  234. data/lib/rubocop/cop/util.rb +22 -6
  235. data/lib/rubocop/cops_documentation_generator.rb +17 -5
  236. data/lib/rubocop/formatter/git_hub_actions_formatter.rb +1 -1
  237. data/lib/rubocop/magic_comment.rb +44 -15
  238. data/lib/rubocop/options.rb +127 -113
  239. data/lib/rubocop/rake_task.rb +1 -1
  240. data/lib/rubocop/result_cache.rb +3 -3
  241. data/lib/rubocop/rspec/cop_helper.rb +1 -1
  242. data/lib/rubocop/rspec/expect_offense.rb +6 -2
  243. data/lib/rubocop/rspec/parallel_formatter.rb +90 -0
  244. data/lib/rubocop/rspec/support.rb +1 -0
  245. data/lib/rubocop/runner.rb +2 -3
  246. data/lib/rubocop/target_finder.rb +1 -1
  247. data/lib/rubocop/version.rb +1 -1
  248. data/lib/rubocop.rb +14 -2
  249. metadata +21 -5
@@ -93,7 +93,15 @@ module RuboCop
93
93
  end
94
94
 
95
95
  def aligned_assignment?(range, line)
96
- range.source[-1] == '=' && line[range.last_column - 1] == '='
96
+ (range.source[-1] == '=' && line[range.last_column - 1] == '=') ||
97
+ aligned_with_append_operator?(range, line)
98
+ end
99
+
100
+ def aligned_with_append_operator?(range, line)
101
+ last_column = range.last_column
102
+
103
+ (range.source == '<<' && line[last_column - 1] == '=') ||
104
+ (range.source[-1] == '=' && line[(last_column - 2)..(last_column - 1)] == '<<')
97
105
  end
98
106
 
99
107
  def aligned_identical?(range, line)
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Ensure a require statement is present for a standard library determined
6
+ # by variable library_name
7
+ module RequireLibrary
8
+ extend NodePattern::Macros
9
+
10
+ def ensure_required(corrector, node, library_name)
11
+ node = node.parent while node.parent&.parent?
12
+
13
+ if node.parent&.begin_type?
14
+ return if @required_libs.include?(library_name)
15
+
16
+ remove_subsequent_requires(corrector, node, library_name)
17
+ end
18
+
19
+ RequireLibraryCorrector.correct(corrector, node, library_name)
20
+ end
21
+
22
+ def remove_subsequent_requires(corrector, node, library_name)
23
+ node.right_siblings.each do |sibling|
24
+ next unless require_library_name?(sibling, library_name)
25
+
26
+ range = range_by_whole_lines(sibling.source_range, include_final_newline: true)
27
+ corrector.remove(range)
28
+ end
29
+ end
30
+
31
+ def on_send(node)
32
+ return if node.parent&.parent?
33
+
34
+ name = require_any_library?(node)
35
+ return if name.nil?
36
+
37
+ @required_libs.add(name)
38
+ end
39
+
40
+ private
41
+
42
+ def on_new_investigation
43
+ # Holds the required files at top-level
44
+ @required_libs = Set.new
45
+ super
46
+ end
47
+
48
+ # @!method require_any_library?(node)
49
+ def_node_matcher :require_any_library?, <<~PATTERN
50
+ (send {(const {nil? cbase} :Kernel) nil?} :require (str $_))
51
+ PATTERN
52
+
53
+ # @!method require_library_name?(node, library_name)
54
+ def_node_matcher :require_library_name?, <<~PATTERN
55
+ (send {(const {nil? cbase} :Kernel) nil?} :require (str %1))
56
+ PATTERN
57
+ end
58
+ end
59
+ end
@@ -28,7 +28,7 @@ module RuboCop
28
28
  end
29
29
 
30
30
  def space_missing?(token1, token2)
31
- token1.line == token2.line && token2.column == token1.column + offset
31
+ same_line?(token1, token2) && token2.column == token1.column + offset
32
32
  end
33
33
 
34
34
  def space_required_before?(token)
@@ -10,7 +10,7 @@ module RuboCop
10
10
  MSG = 'Space found before %<token>s.'
11
11
 
12
12
  def on_new_investigation
13
- each_missing_space(processed_source.tokens) do |token, pos_before|
13
+ each_missing_space(processed_source.sorted_tokens) do |token, pos_before|
14
14
  add_offense(pos_before, message: format(MSG, token: kind(token))) do |corrector|
15
15
  PunctuationCorrector.remove_space(corrector, pos_before)
16
16
  end
@@ -32,7 +32,7 @@ module RuboCop
32
32
  end
33
33
 
34
34
  def space_missing?(token1, token2)
35
- token1.line == token2.line && token2.begin_pos > token1.end_pos
35
+ same_line?(token1, token2) && token2.begin_pos > token1.end_pos
36
36
  end
37
37
 
38
38
  def space_required_after?(token)
@@ -54,7 +54,7 @@ module RuboCop
54
54
  end
55
55
 
56
56
  def first_line_comment(node)
57
- comment = processed_source.find_comment { |c| c.loc.line == node.loc.line }
57
+ comment = processed_source.find_comment { |c| same_line?(c, node) }
58
58
  return unless comment
59
59
 
60
60
  comment_source = comment.loc.expression.source
@@ -13,7 +13,11 @@ module RuboCop
13
13
  if style == :single_quotes
14
14
  !double_quotes_required?(src)
15
15
  else
16
- !/" | \\[^'\\] | \#[@{$]/x.match?(src)
16
+ # The string needs single quotes if:
17
+ # 1. It contains a double quote
18
+ # 2. It contains text that would become an escape sequence with double quotes
19
+ # 3. It contains text that would become an interpolation with double quotes
20
+ !/" | (?<!\\)\\[aAbcdefkMnprsStuUxzZ0-7] | \#[@{$]/x.match?(src)
17
21
  end
18
22
  end
19
23
  end
@@ -10,7 +10,7 @@ module RuboCop
10
10
  end
11
11
 
12
12
  def body_on_first_line?(node, body)
13
- node.source_range.first_line == body.source_range.first_line
13
+ same_line?(node, body)
14
14
  end
15
15
 
16
16
  def first_part_of(body)
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # rubocop:disable Style/AsciiComments
4
-
5
3
  module RuboCop
6
4
  module Cop
7
5
  module Naming
@@ -90,4 +88,3 @@ module RuboCop
90
88
  end
91
89
  end
92
90
  end
93
- # rubocop:enable Style/AsciiComments
@@ -24,7 +24,7 @@ module RuboCop
24
24
  # # With `AllowNamesEndingInNumbers` set to false
25
25
  # foo { |num1, num2| num1 * num2 }
26
26
  #
27
- # # With `MinParamNameLength` set to number greater than 1
27
+ # # With `MinNameLength` set to number greater than 1
28
28
  # baz { |a, b, c| do_stuff(a, b, c) }
29
29
  #
30
30
  # # good
@@ -54,7 +54,7 @@ module RuboCop
54
54
  private
55
55
 
56
56
  def allowed_assignment?(value)
57
- value && %i[block const casgn].include?(value.type) ||
57
+ (value && %i[block const casgn].include?(value.type)) ||
58
58
  allowed_method_call_on_rhs?(value) ||
59
59
  class_or_struct_return_method?(value) ||
60
60
  allowed_conditional_expression_on_rhs?(value)
@@ -19,6 +19,8 @@ module RuboCop
19
19
  # Regex can be specified to identify offenses. Suggestions for replacing a flagged term can
20
20
  # be configured and will be displayed as part of the offense message.
21
21
  # An AllowedRegex can be specified for a flagged term to exempt allowed uses of the term.
22
+ # `WholeWord: true` can be set on a flagged term to indicate the cop should only match when
23
+ # a term matches the whole word (partial matches will not be offenses).
22
24
  #
23
25
  # @example FlaggedTerms: { whitelist: { Suggestions: ['allowlist'] } }
24
26
  # # Suggest replacing identifier whitelist with allowlist
@@ -56,10 +58,20 @@ module RuboCop
56
58
  # # good
57
59
  # # They had a master's degree
58
60
  #
61
+ # @example FlaggedTerms: { slave: { WholeWord: true } }
62
+ # # Specify that only terms that are full matches will be flagged.
63
+ #
64
+ # # bad
65
+ # Slave
66
+ #
67
+ # # good (won't be flagged despite containing `slave`)
68
+ # TeslaVehicle
59
69
  class InclusiveLanguage < Base
60
70
  include RangeHelp
61
71
 
62
72
  EMPTY_ARRAY = [].freeze
73
+ MSG = "Consider replacing '%<term>s'%<suffix>s."
74
+ MSG_FOR_FILE_PATH = "Consider replacing '%<term>s' in file path%<suffix>s."
63
75
 
64
76
  WordLocation = Struct.new(:word, :position)
65
77
 
@@ -123,7 +135,7 @@ module RuboCop
123
135
  next if term_definition.nil?
124
136
 
125
137
  allowed_strings.concat(process_allowed_regex(term_definition['AllowedRegex']))
126
- regex_string = ensure_regex_string(term_definition['Regex'] || term)
138
+ regex_string = ensure_regex_string(extract_regexp(term, term_definition))
127
139
  flagged_term_strings << regex_string
128
140
 
129
141
  add_to_flagged_term_hash(regex_string, term, term_definition)
@@ -132,6 +144,13 @@ module RuboCop
132
144
  set_regexes(flagged_term_strings, allowed_strings)
133
145
  end
134
146
 
147
+ def extract_regexp(term, term_definition)
148
+ return term_definition['Regex'] if term_definition['Regex']
149
+ return /(?:\b|(?<=[\W_]))#{term}(?:\b|(?=[\W_]))/ if term_definition['WholeWord']
150
+
151
+ term
152
+ end
153
+
135
154
  def add_to_flagged_term_hash(regex_string, term, term_definition)
136
155
  @flagged_term_hash[Regexp.new(regex_string, Regexp::IGNORECASE)] =
137
156
  term_definition.merge('Term' => term,
@@ -180,12 +199,11 @@ module RuboCop
180
199
  end
181
200
 
182
201
  def create_single_word_message_for_file(word)
183
- create_message(word).sub(/\.$/, ' in file path.')
202
+ create_message(word, MSG_FOR_FILE_PATH)
184
203
  end
185
204
 
186
205
  def create_multiple_word_message_for_file(words)
187
- quoted_words = words.map { |word| "'#{word}'" }
188
- "Consider replacing problematic terms #{quoted_words.join(', ')} in file path."
206
+ format(MSG_FOR_FILE_PATH, term: words.join("', '"), suffix: ' with other terms')
189
207
  end
190
208
 
191
209
  def scan_for_words(input)
@@ -206,9 +224,12 @@ module RuboCop
206
224
  safe_str.gsub(@allowed_regex) { |match| '*' * match.size }
207
225
  end
208
226
 
209
- def create_message(word)
227
+ def create_message(word, message = MSG)
210
228
  flagged_term = find_flagged_term(word)
211
- "Consider replacing problematic term '#{word}'#{flagged_term['SuggestionString']}."
229
+ suggestions = flagged_term['SuggestionString']
230
+ suggestions = ' with another term' if suggestions.blank?
231
+
232
+ format(message, term: word, suffix: suggestions)
212
233
  end
213
234
 
214
235
  def find_flagged_term(word)
@@ -218,10 +239,6 @@ module RuboCop
218
239
  flagged_term
219
240
  end
220
241
 
221
- def create_message_for_file(word)
222
- create_message(word).sub(/\.$/, ' in file path.')
223
- end
224
-
225
242
  def preprocess_suggestions(suggestions)
226
243
  return '' if suggestions.nil? ||
227
244
  (suggestions.is_a?(String) && suggestions.strip.empty?) || suggestions.empty?
@@ -14,6 +14,11 @@ module RuboCop
14
14
  # convention that is used to implicitly indicate that an ivar should not
15
15
  # be set or referenced outside of the memoization method.
16
16
  #
17
+ # @safety
18
+ # This cop relies on the pattern `@instance_var ||= ...`,
19
+ # but this is sometimes used for other purposes than memoization
20
+ # so this cop is considered unsafe.
21
+ #
17
22
  # @example EnforcedStyleForLeadingUnderscores: disallowed (default)
18
23
  # # bad
19
24
  # # Method foo is memoized using an instance variable that is
@@ -139,10 +144,6 @@ module RuboCop
139
144
  # define_method(:foo) do
140
145
  # @_foo ||= calculate_expensive_thing
141
146
  # end
142
- #
143
- # This cop relies on the pattern `@instance_var ||= ...`,
144
- # but this is sometimes used for other purposes than memoization
145
- # so this cop is considered unsafe.
146
147
  class MemoizedInstanceVariableName < Base
147
148
  include ConfigurableEnforcedStyle
148
149
 
@@ -75,6 +75,9 @@ module RuboCop
75
75
  preferred_name = preferred_name(offending_name)
76
76
  return if preferred_name.to_sym == offending_name
77
77
 
78
+ # check variable shadowing for exception variable
79
+ return if shadowed_variable_name?(node)
80
+
78
81
  range = offense_range(node)
79
82
  message = message(node)
80
83
 
@@ -150,6 +153,10 @@ module RuboCop
150
153
  preferred_name = preferred_name(offending_name)
151
154
  format(MSG, preferred: preferred_name, bad: offending_name)
152
155
  end
156
+
157
+ def shadowed_variable_name?(node)
158
+ node.each_descendant(:lvar).any? { |n| n.children.first.to_s == preferred_name(n) }
159
+ end
153
160
  end
154
161
  end
155
162
  end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Security
6
+ # Checks for the first argument to `IO.read`, `IO.binread`, `IO.write`, `IO.binwrite`,
7
+ # `IO.foreach`, and `IO.readlines`.
8
+ #
9
+ # If argument starts with a pipe character (`'|'`) and the receiver is the `IO` class,
10
+ # a subprocess is created in the same way as `Kernel#open`, and its output is returned.
11
+ # `Kernel#open` may allow unintentional command injection, which is the reason these
12
+ # `IO` methods are a security risk.
13
+ # Consider to use `File.read` to disable the behavior of subprocess invocation.
14
+ #
15
+ # @safety
16
+ # This cop is unsafe because false positive will occur if the variable passed as
17
+ # the first argument is a command that is not a file path.
18
+ #
19
+ # @example
20
+ #
21
+ # # bad
22
+ # IO.read(path)
23
+ # IO.read('path')
24
+ #
25
+ # # good
26
+ # File.read(path)
27
+ # File.read('path')
28
+ # IO.read('| command') # Allow intentional command invocation.
29
+ #
30
+ class IoMethods < Base
31
+ extend AutoCorrector
32
+
33
+ MSG = '`File.%<method_name>s` is safer than `IO.%<method_name>s`.'
34
+ RESTRICT_ON_SEND = %i[read binread write binwrite foreach readlines].freeze
35
+
36
+ def on_send(node)
37
+ return unless (receiver = node.receiver) && receiver.source == 'IO'
38
+
39
+ argument = node.first_argument
40
+ return if argument.respond_to?(:value) && argument.value.strip.start_with?('|')
41
+
42
+ add_offense(node, message: format(MSG, method_name: node.method_name)) do |corrector|
43
+ corrector.replace(receiver, 'File')
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -6,13 +6,14 @@ module RuboCop
6
6
  # This cop checks for the use of JSON class methods which have potential
7
7
  # security issues.
8
8
  #
9
- # Autocorrect is disabled by default because it's potentially dangerous.
10
- # If using a stream, like `JSON.load(open('file'))`, it will need to call
11
- # `#read` manually, like `JSON.parse(open('file').read)`.
12
- # If reading single values (rather than proper JSON objects), like
13
- # `JSON.load('false')`, it will need to pass the `quirks_mode: true`
14
- # option, like `JSON.parse('false', quirks_mode: true)`.
15
- # Other similar issues may apply.
9
+ # @safety
10
+ # This cop's autocorrection is unsafe because it's potentially dangerous.
11
+ # If using a stream, like `JSON.load(open('file'))`, it will need to call
12
+ # `#read` manually, like `JSON.parse(open('file').read)`.
13
+ # If reading single values (rather than proper JSON objects), like
14
+ # `JSON.load('false')`, it will need to pass the `quirks_mode: true`
15
+ # option, like `JSON.parse('false', quirks_mode: true)`.
16
+ # Other similar issues may apply.
16
17
  #
17
18
  # @example
18
19
  # # bad
@@ -11,6 +11,10 @@ module RuboCop
11
11
  # the argument of `Kernel#open` and `URI.open`. It would be better to use
12
12
  # `File.open`, `IO.popen` or `URI.parse#open` explicitly.
13
13
  #
14
+ # @safety
15
+ # This cop could register false positives if `open` is redefined
16
+ # in a class and then used without a receiver in that class.
17
+ #
14
18
  # @example
15
19
  # # bad
16
20
  # open(something)
@@ -7,6 +7,10 @@ module RuboCop
7
7
  # potential security issues leading to remote code execution when
8
8
  # loading from an untrusted source.
9
9
  #
10
+ # @safety
11
+ # The behaviour of the code might change depending on what was
12
+ # in the YAML payload, since `YAML.safe_load` is more restrictive.
13
+ #
10
14
  # @example
11
15
  # # bad
12
16
  # YAML.load("--- foo")
@@ -59,8 +59,8 @@ module RuboCop
59
59
 
60
60
  def check(send_node)
61
61
  return if previous_line_comment?(send_node)
62
- return unless grouped_style? && sibling_accessors(send_node).size > 1 ||
63
- separated_style? && send_node.arguments.size > 1
62
+ return unless (grouped_style? && sibling_accessors(send_node).size > 1) ||
63
+ (separated_style? && send_node.arguments.size > 1)
64
64
 
65
65
  message = message(send_node)
66
66
  add_offense(send_node, message: message) do |corrector|
@@ -7,6 +7,11 @@ module RuboCop
7
7
  # `||` instead. It can be configured to check only in conditions or in
8
8
  # all contexts.
9
9
  #
10
+ # @safety
11
+ # Auto-correction is unsafe because there is a different operator precedence
12
+ # between logical operators (`&&` and `||`) and semantic operators (`and` and `or`),
13
+ # and that might change the behaviour.
14
+ #
10
15
  # @example EnforcedStyle: always
11
16
  # # bad
12
17
  # foo.save and return
@@ -30,6 +30,10 @@ module RuboCop
30
30
  # bar(*args)
31
31
  # end
32
32
  #
33
+ # def foo(**kwargs)
34
+ # bar(**kwargs)
35
+ # end
36
+ #
33
37
  # @example AllowOnlyRestArgument: false
34
38
  # # bad
35
39
  # # The following code can replace the arguments with `...`,
@@ -38,6 +42,10 @@ module RuboCop
38
42
  # bar(*args)
39
43
  # end
40
44
  #
45
+ # def foo(**kwargs)
46
+ # bar(**kwargs)
47
+ # end
48
+ #
41
49
  class ArgumentsForwarding < Base
42
50
  include RangeHelp
43
51
  extend AutoCorrector
@@ -49,12 +57,15 @@ module RuboCop
49
57
 
50
58
  # @!method use_rest_arguments?(node)
51
59
  def_node_matcher :use_rest_arguments?, <<~PATTERN
52
- (args (restarg $_) $...)
60
+ (args ({restarg kwrestarg} $_) $...)
53
61
  PATTERN
54
62
 
55
63
  # @!method only_rest_arguments?(node, name)
56
64
  def_node_matcher :only_rest_arguments?, <<~PATTERN
57
- (send _ _ (splat (lvar %1)))
65
+ {
66
+ (send _ _ (splat (lvar %1)))
67
+ (send _ _ (hash (kwsplat (lvar %1))))
68
+ }
58
69
  PATTERN
59
70
 
60
71
  # @!method forwarding_method_arguments?(node, rest_name, block_name, kwargs_name)
@@ -5,9 +5,27 @@ module RuboCop
5
5
  module Style
6
6
  # This cop enforces the use of `Array()` instead of explicit `Array` check or `[*var]`.
7
7
  #
8
- # This cop is disabled by default because false positive will occur if
9
- # the argument of `Array()` is not an array (e.g. Hash, Set),
10
- # an array will be returned as an incompatibility result.
8
+ # The cop is disabled by default due to safety concerns.
9
+ #
10
+ # @safety
11
+ # This cop is unsafe because a false positive may occur if
12
+ # the argument of `Array()` is (or could be) nil or depending
13
+ # on how the argument is handled by `Array()` (which can be
14
+ # different than just wrapping the argument in an array).
15
+ #
16
+ # For example:
17
+ #
18
+ # [source,ruby]
19
+ # ----
20
+ # [nil] #=> [nil]
21
+ # Array(nil) #=> []
22
+ #
23
+ # [{a: 'b'}] #= [{a: 'b'}]
24
+ # Array({a: 'b'}) #=> [[:a, 'b']]
25
+ #
26
+ # [Time.now] #=> [#<Time ...>]
27
+ # Array(Time.now) #=> [14, 16, 14, 16, 9, 2021, 4, 259, true, "EDT"]
28
+ # ----
11
29
  #
12
30
  # @example
13
31
  # # bad
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # rubocop:disable Style/AsciiComments
4
-
5
3
  module RuboCop
6
4
  module Cop
7
5
  module Style
@@ -57,4 +55,3 @@ module RuboCop
57
55
  end
58
56
  end
59
57
  end
60
- # rubocop:enable Style/AsciiComments
@@ -1,11 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # rubocop:disable Metrics/ClassLength
3
4
  module RuboCop
4
5
  module Cop
5
6
  module Style
6
7
  # Check for uses of braces or do/end around single line or
7
8
  # multi-line blocks.
8
9
  #
10
+ # Methods that can be either procedural or functional and cannot be
11
+ # categorised from their usage alone is ignored.
12
+ # `lambda`, `proc`, and `it` are their defaults.
13
+ # Additional methods can be added to the `IgnoredMethods`.
14
+ #
9
15
  # @example EnforcedStyle: line_count_based (default)
10
16
  # # bad - single line block
11
17
  # items.each do |item| item / 5 end
@@ -132,9 +138,21 @@ module RuboCop
132
138
  # puts foo
133
139
  # end
134
140
  #
141
+ # @example IgnoredMethods: ['lambda', 'proc', 'it' ] (default)
142
+ #
143
+ # # good
144
+ # foo = lambda do |x|
145
+ # puts "Hello, #{x}"
146
+ # end
147
+ #
148
+ # foo = lambda do |x|
149
+ # x * 100
150
+ # end
151
+ #
135
152
  class BlockDelimiters < Base
136
153
  include ConfigurableEnforcedStyle
137
154
  include IgnoredMethods
155
+ include RangeHelp
138
156
  extend AutoCorrector
139
157
 
140
158
  ALWAYS_BRACES_MESSAGE = 'Prefer `{...}` over `do...end` for blocks.'
@@ -174,7 +192,7 @@ module RuboCop
174
192
  if node.braces?
175
193
  replace_braces_with_do_end(corrector, node.loc)
176
194
  else
177
- replace_do_end_with_braces(corrector, node.loc)
195
+ replace_do_end_with_braces(corrector, node)
178
196
  end
179
197
  end
180
198
 
@@ -231,10 +249,16 @@ module RuboCop
231
249
  corrector.insert_before(e, ' ') unless whitespace_before?(e)
232
250
  corrector.insert_after(b, ' ') unless whitespace_after?(b)
233
251
  corrector.replace(b, 'do')
252
+
253
+ if (comment = processed_source.comment_at_line(e.line))
254
+ move_comment_before_block(corrector, comment, loc.node, e)
255
+ end
256
+
234
257
  corrector.replace(e, 'end')
235
258
  end
236
259
 
237
- def replace_do_end_with_braces(corrector, loc)
260
+ def replace_do_end_with_braces(corrector, node)
261
+ loc = node.loc
238
262
  b = loc.begin
239
263
  e = loc.end
240
264
 
@@ -242,6 +266,8 @@ module RuboCop
242
266
 
243
267
  corrector.replace(b, '{')
244
268
  corrector.replace(e, '}')
269
+
270
+ corrector.wrap(node.body, "begin\n", "\nend") if begin_required?(node)
245
271
  end
246
272
 
247
273
  def whitespace_before?(range)
@@ -252,6 +278,21 @@ module RuboCop
252
278
  /\s/.match?(range.source_buffer.source[range.begin_pos + length, 1])
253
279
  end
254
280
 
281
+ def move_comment_before_block(corrector, comment, block_node, closing_brace)
282
+ range = block_node.chained? ? end_of_chain(block_node.parent).source_range : closing_brace
283
+ comment_range = range_between(range.end_pos, comment.loc.expression.end_pos)
284
+ corrector.remove(range_with_surrounding_space(range: comment_range, side: :right))
285
+ corrector.insert_after(range, "\n")
286
+
287
+ corrector.insert_before(block_node, "#{comment.text}\n")
288
+ end
289
+
290
+ def end_of_chain(node)
291
+ return node unless node.chained?
292
+
293
+ end_of_chain(node.parent)
294
+ end
295
+
255
296
  def get_blocks(node, &block)
256
297
  case node.type
257
298
  when :block
@@ -376,7 +417,14 @@ module RuboCop
376
417
  def array_or_range?(node)
377
418
  node.array_type? || node.range_type?
378
419
  end
420
+
421
+ def begin_required?(block_node)
422
+ # If the block contains `rescue` or `ensure`, it needs to be wrapped in
423
+ # `begin`...`end` when changing `do-end` to `{}`.
424
+ block_node.each_child_node(:rescue, :ensure).any? && !block_node.single_line?
425
+ end
379
426
  end
380
427
  end
381
428
  end
382
429
  end
430
+ # rubocop:enable Metrics/ClassLength
@@ -5,9 +5,11 @@ module RuboCop
5
5
  module Style
6
6
  # This cop checks for uses of the case equality operator(===).
7
7
  #
8
+ # If `AllowOnConstant` option is enabled, the cop will ignore violations when the receiver of
9
+ # the case equality operator is a constant.
10
+ #
8
11
  # @example
9
12
  # # bad
10
- # Array === something
11
13
  # (1..100) === 7
12
14
  # /something/ === some_string
13
15
  #
@@ -16,18 +18,13 @@ module RuboCop
16
18
  # (1..100).include?(7)
17
19
  # /something/.match?(some_string)
18
20
  #
19
- # @example AllowOnConstant
20
- # # Style/CaseEquality:
21
- # # AllowOnConstant: true
22
- #
21
+ # @example AllowOnConstant: false (default)
23
22
  # # bad
24
- # (1..100) === 7
25
- # /something/ === some_string
23
+ # Array === something
26
24
  #
25
+ # @example AllowOnConstant: true
27
26
  # # good
28
27
  # Array === something
29
- # (1..100).include?(7)
30
- # /something/.match?(some_string)
31
28
  #
32
29
  class CaseEquality < Base
33
30
  extend AutoCorrector