rubocop 1.18.1 → 1.22.3

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