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
@@ -88,7 +88,7 @@ module RuboCop
88
88
  end
89
89
 
90
90
  def check_new_line_offense(node, rhs)
91
- return unless node.loc.operator.line == rhs.first_line
91
+ return unless same_line?(node.loc.operator, rhs)
92
92
 
93
93
  add_offense(node, message: NEW_LINE_OFFENSE) do |corrector|
94
94
  corrector.insert_after(node.loc.operator, "\n")
@@ -63,7 +63,7 @@ module RuboCop
63
63
  add_offense_for_expression(node, node.arguments, ARG_MSG)
64
64
  end
65
65
 
66
- return unless node.body && node.loc.begin.line == node.body.first_line
66
+ return unless node.body && same_line?(node.loc.begin, node.body)
67
67
 
68
68
  add_offense_for_expression(node, node.body, MSG)
69
69
  end
@@ -89,7 +89,7 @@ module RuboCop
89
89
  if node.source.lines.first.end_with?("|\n")
90
90
  PIPE_SIZE
91
91
  else
92
- 1 + PIPE_SIZE * 2
92
+ 1 + (PIPE_SIZE * 2)
93
93
  end
94
94
  end
95
95
 
@@ -110,7 +110,7 @@ module RuboCop
110
110
 
111
111
  expr_before_body ||= node.loc.begin
112
112
 
113
- return unless expr_before_body.line == node.body.first_line
113
+ return unless same_line?(expr_before_body, node.body)
114
114
 
115
115
  autocorrect_body(corrector, node, node.body)
116
116
  end
@@ -6,6 +6,9 @@ module RuboCop
6
6
  # This cop ensures that each argument in a multi-line method call
7
7
  # starts on a separate line.
8
8
  #
9
+ # NOTE: this cop does not move the first argument, if you want that to
10
+ # be on a separate line, see `Layout/FirstMethodArgumentLineBreak`.
11
+ #
9
12
  # @example
10
13
  #
11
14
  # # bad
@@ -75,7 +75,7 @@ module RuboCop
75
75
  def right_hand_side(send_node)
76
76
  dot = send_node.loc.dot
77
77
  selector = send_node.loc.selector
78
- if send_node.dot? && selector && dot.line == selector.line
78
+ if send_node.dot? && selector && same_line?(dot, selector)
79
79
  dot.join(selector)
80
80
  elsif selector
81
81
  selector
@@ -127,6 +127,7 @@ module RuboCop
127
127
  .gsub(/" *\\\n\s*'/, %q(" + ')) # Double quote, backslash, and then single quote
128
128
  .gsub(/' *\\\n\s*"/, %q(' + ")) # Single quote, backslash, and then double quote
129
129
  .gsub(/(["']) *\\\n\s*\1/, '') # Double or single quote, backslash, then same quote
130
+ .gsub(/\n\s*(?=\.\w)/, '') # Extra space within method chaining
130
131
  .gsub(/\s*\\?\n\s*/, ' ') # Any other line break, with or without backslash
131
132
  end
132
133
 
@@ -29,8 +29,7 @@ module RuboCop
29
29
  MSG = '`%<kw_loc>s` at %<kw_loc_line>d, %<kw_loc_column>d is not ' \
30
30
  'aligned with `%<beginning>s` at ' \
31
31
  '%<begin_loc_line>d, %<begin_loc_column>d.'
32
- ANCESTOR_TYPES = %i[kwbegin def defs class module].freeze
33
- RUBY_2_5_ANCESTOR_TYPES = (ANCESTOR_TYPES + %i[block]).freeze
32
+ ANCESTOR_TYPES = %i[kwbegin def defs class module block].freeze
34
33
  ANCESTOR_TYPES_WITH_ACCESS_MODIFIERS = %i[def defs].freeze
35
34
  ALTERNATIVE_ACCESS_MODIFIERS = %i[public_class_method private_class_method].freeze
36
35
 
@@ -62,7 +61,7 @@ module RuboCop
62
61
  alignment_loc = alignment_location(alignment_node)
63
62
  kw_loc = node.loc.keyword
64
63
 
65
- return if alignment_loc.column == kw_loc.column || alignment_loc.line == kw_loc.line
64
+ return if alignment_loc.column == kw_loc.column || same_line?(alignment_loc, kw_loc)
66
65
 
67
66
  add_offense(
68
67
  kw_loc, message: format_message(alignment_node, alignment_loc, kw_loc)
@@ -118,6 +117,8 @@ module RuboCop
118
117
  ancestor_node = ancestor_node(node)
119
118
 
120
119
  return ancestor_node if ancestor_node.nil? || ancestor_node.kwbegin_type?
120
+ return if ancestor_node.respond_to?(:send_node) &&
121
+ aligned_with_line_break_method?(ancestor_node, node)
121
122
 
122
123
  assignment_node = assignment_node(ancestor_node)
123
124
  return assignment_node if same_line?(ancestor_node, assignment_node)
@@ -129,14 +130,26 @@ module RuboCop
129
130
  end
130
131
 
131
132
  def ancestor_node(node)
132
- ancestor_types =
133
- if target_ruby_version >= 2.5
134
- RUBY_2_5_ANCESTOR_TYPES
135
- else
136
- ANCESTOR_TYPES
137
- end
133
+ node.each_ancestor(*ANCESTOR_TYPES).first
134
+ end
135
+
136
+ def aligned_with_line_break_method?(ancestor_node, node)
137
+ send_node_loc = ancestor_node.send_node.loc
138
+ do_keyword_line = ancestor_node.loc.begin.line
139
+ rescue_keyword_column = node.loc.keyword.column
140
+ selector = send_node_loc.respond_to?(:selector) ? send_node_loc.selector : send_node_loc
141
+
142
+ if aligned_with_leading_dot?(do_keyword_line, send_node_loc, rescue_keyword_column)
143
+ return true
144
+ end
145
+
146
+ do_keyword_line == selector.line && rescue_keyword_column == selector.column
147
+ end
148
+
149
+ def aligned_with_leading_dot?(do_keyword_line, send_node_loc, rescue_keyword_column)
150
+ return false unless send_node_loc.respond_to?(:dot) && (dot = send_node_loc.dot)
138
151
 
139
- node.each_ancestor(*ancestor_types).first
152
+ do_keyword_line == dot.line && rescue_keyword_column == dot.column
140
153
  end
141
154
 
142
155
  def assignment_node(node)
@@ -37,15 +37,26 @@ module RuboCop
37
37
  return unless receiver&.block_type?
38
38
 
39
39
  receiver_location = receiver.loc
40
- closing_block_delimiter_line_number = receiver_location.end.line
41
- return if receiver_location.begin.line < closing_block_delimiter_line_number
40
+ closing_block_delimiter_line_num = receiver_location.end.line
41
+ return if receiver_location.begin.line < closing_block_delimiter_line_num
42
42
 
43
43
  node_location = node.loc
44
44
  dot_range = node_location.dot
45
45
  return unless dot_range
46
- return if dot_range.line > closing_block_delimiter_line_number
46
+ return unless call_method_after_block?(node, dot_range, closing_block_delimiter_line_num)
47
47
 
48
- range_between(dot_range.begin_pos, node_location.selector.end_pos)
48
+ range_between(dot_range.begin_pos, selector_range(node).end_pos)
49
+ end
50
+
51
+ def call_method_after_block?(node, dot_range, closing_block_delimiter_line_num)
52
+ return false if dot_range.line > closing_block_delimiter_line_num
53
+
54
+ dot_range.column < selector_range(node).column
55
+ end
56
+
57
+ def selector_range(node)
58
+ # l.(1) has no selector, so we use the opening parenthesis instead
59
+ node.loc.selector || node.loc.begin
49
60
  end
50
61
  end
51
62
  end
@@ -16,6 +16,7 @@ module RuboCop
16
16
  extend AutoCorrector
17
17
 
18
18
  MSG = 'Do not leave space between `!` and its argument.'
19
+ RESTRICT_ON_SEND = %i[!].freeze
19
20
 
20
21
  def on_send(node)
21
22
  return unless node.prefix_bang? && whitespace_after_operator?(node)
@@ -47,7 +47,8 @@ module RuboCop
47
47
  space_on_both_sides = space_on_both_sides?(arg, equals)
48
48
  no_surrounding_space = no_surrounding_space?(arg, equals)
49
49
 
50
- if style == :space && space_on_both_sides || style == :no_space && no_surrounding_space
50
+ if (style == :space && space_on_both_sides) ||
51
+ (style == :no_space && no_surrounding_space)
51
52
  correct_style_detected
52
53
  else
53
54
  incorrect_style_detected(arg, value)
@@ -228,8 +228,8 @@ module RuboCop
228
228
  def accepted_opening_delimiter?(range, char)
229
229
  return true unless char
230
230
 
231
- accept_left_square_bracket?(range) && char == '[' ||
232
- accept_left_parenthesis?(range) && char == '('
231
+ (accept_left_square_bracket?(range) && char == '[') ||
232
+ (accept_left_parenthesis?(range) && char == '(')
233
233
  end
234
234
 
235
235
  def accept_left_parenthesis?(range)
@@ -63,6 +63,10 @@ module RuboCop
63
63
  [Style::SelfAssignment]
64
64
  end
65
65
 
66
+ def on_sclass(node)
67
+ check_operator(:sclass, node.loc.operator, node.source_range)
68
+ end
69
+
66
70
  def on_pair(node)
67
71
  return unless node.hash_rocket?
68
72
 
@@ -104,6 +108,14 @@ module RuboCop
104
108
  check_operator(:assignment, node.loc.operator, rhs.source_range)
105
109
  end
106
110
 
111
+ def on_casgn(node)
112
+ _, _, right, = *node
113
+
114
+ return unless right
115
+
116
+ check_operator(:assignment, node.loc.operator, right.source_range)
117
+ end
118
+
107
119
  def on_binary(node)
108
120
  _, rhs, = *node
109
121
 
@@ -130,7 +142,6 @@ module RuboCop
130
142
  alias on_and on_binary
131
143
  alias on_lvasgn on_assignment
132
144
  alias on_masgn on_assignment
133
- alias on_casgn on_special_asgn
134
145
  alias on_ivasgn on_assignment
135
146
  alias on_cvasgn on_assignment
136
147
  alias on_gvasgn on_assignment
@@ -19,6 +19,7 @@ module RuboCop
19
19
  extend AutoCorrector
20
20
 
21
21
  MSG = 'Remove the space before the opening brackets.'
22
+ RESTRICT_ON_SEND = %i[[] []=].freeze
22
23
 
23
24
  def on_send(node)
24
25
  return unless (first_argument = node.first_argument)
@@ -18,9 +18,9 @@ module RuboCop
18
18
  MSG = 'Put a space before an end-of-line comment.'
19
19
 
20
20
  def on_new_investigation
21
- processed_source.tokens.each_cons(2) do |token1, token2|
21
+ processed_source.sorted_tokens.each_cons(2) do |token1, token2|
22
22
  next unless token2.comment?
23
- next unless token1.line == token2.line
23
+ next unless same_line?(token1, token2)
24
24
  next unless token1.pos.end == token2.pos.begin
25
25
 
26
26
  range = token2.pos
@@ -33,6 +33,27 @@ module RuboCop
33
33
  # g = ( a + 3 )
34
34
  # y()
35
35
  #
36
+ # @example EnforcedStyle: compact
37
+ # # The `compact` style enforces that parentheses have a space at the
38
+ # # beginning with the exception that successive parentheses are allowed.
39
+ # # Note: Empty parentheses should not have spaces.
40
+ #
41
+ # # bad
42
+ # f(3)
43
+ # g = (a + 3)
44
+ # y( )
45
+ # g( f( x ) )
46
+ # g( f( x( 3 ) ), 5 )
47
+ # g( ( ( 3 + 5 ) * f) ** x, 5 )
48
+ #
49
+ # # good
50
+ # f( 3 )
51
+ # g = ( a + 3 )
52
+ # y()
53
+ # g( f( x ))
54
+ # g( f( x( 3 )), 5 )
55
+ # g((( 3 + 5 ) * f ) ** x, 5 )
56
+ #
36
57
  class SpaceInsideParens < Base
37
58
  include SurroundingSpace
38
59
  include RangeHelp
@@ -43,37 +64,39 @@ module RuboCop
43
64
  MSG_SPACE = 'No space inside parentheses detected.'
44
65
 
45
66
  def on_new_investigation
46
- @processed_source = processed_source
67
+ tokens = processed_source.sorted_tokens
47
68
 
48
- if style == :space
49
- process_with_space_style(processed_source)
69
+ case style
70
+ when :space
71
+ process_with_space_style(tokens)
72
+ when :compact
73
+ process_with_compact_style(tokens)
50
74
  else
51
- each_extraneous_space(processed_source.tokens) do |range|
52
- add_offense(range) do |corrector|
53
- corrector.remove(range)
54
- end
55
- end
75
+ correct_extraneous_space(tokens)
56
76
  end
57
77
  end
58
78
 
59
79
  private
60
80
 
61
- def process_with_space_style(processed_source)
62
- processed_source.tokens.each_cons(2) do |token1, token2|
63
- each_extraneous_space_in_empty_parens(token1, token2) do |range|
64
- add_offense(range) do |corrector|
65
- corrector.remove(range)
66
- end
67
- end
68
- each_missing_space(token1, token2) do |range|
69
- add_offense(range, message: MSG_SPACE) do |corrector|
70
- corrector.insert_before(range, ' ')
71
- end
81
+ def process_with_space_style(tokens)
82
+ tokens.each_cons(2) do |token1, token2|
83
+ correct_extraneous_space_in_empty_parens(token1, token2)
84
+ correct_missing_space(token1, token2)
85
+ end
86
+ end
87
+
88
+ def process_with_compact_style(tokens)
89
+ tokens.each_cons(2) do |token1, token2|
90
+ correct_extraneous_space_in_empty_parens(token1, token2)
91
+ if !left_parens?(token1, token2) && !right_parens?(token1, token2)
92
+ correct_missing_space(token1, token2)
93
+ else
94
+ correct_extaneus_space_between_consecutive_parens(token1, token2)
72
95
  end
73
96
  end
74
97
  end
75
98
 
76
- def each_extraneous_space(tokens)
99
+ def correct_extraneous_space(tokens)
77
100
  tokens.each_cons(2) do |token1, token2|
78
101
  next unless parens?(token1, token2)
79
102
 
@@ -82,36 +105,59 @@ module RuboCop
82
105
  next if token2.comment?
83
106
  next unless same_line?(token1, token2) && token1.space_after?
84
107
 
85
- yield range_between(token1.end_pos, token2.begin_pos)
108
+ range = range_between(token1.end_pos, token2.begin_pos)
109
+ add_offense(range) do |corrector|
110
+ corrector.remove(range)
111
+ end
112
+ end
113
+ end
114
+
115
+ def correct_extaneus_space_between_consecutive_parens(token1, token2)
116
+ return if range_between(token1.end_pos, token2.begin_pos).source != ' '
117
+
118
+ range = range_between(token1.end_pos, token2.begin_pos)
119
+ add_offense(range) do |corrector|
120
+ corrector.remove(range)
86
121
  end
87
122
  end
88
123
 
89
- def each_extraneous_space_in_empty_parens(token1, token2)
124
+ def correct_extraneous_space_in_empty_parens(token1, token2)
90
125
  return unless token1.left_parens? && token2.right_parens?
91
126
 
92
127
  return if range_between(token1.begin_pos, token2.end_pos).source == '()'
93
128
 
94
- yield range_between(token1.end_pos, token2.begin_pos)
129
+ range = range_between(token1.end_pos, token2.begin_pos)
130
+ add_offense(range) do |corrector|
131
+ corrector.remove(range)
132
+ end
95
133
  end
96
134
 
97
- def each_missing_space(token1, token2)
135
+ def correct_missing_space(token1, token2)
98
136
  return if can_be_ignored?(token1, token2)
99
137
 
100
- if token1.left_parens?
101
- yield range_between(token2.begin_pos, token2.begin_pos + 1)
102
- elsif token2.right_parens?
103
- yield range_between(token2.begin_pos, token2.end_pos)
104
- end
105
- end
138
+ range = if token1.left_parens?
139
+ range_between(token2.begin_pos, token2.begin_pos + 1)
140
+ elsif token2.right_parens?
141
+ range_between(token2.begin_pos, token2.end_pos)
142
+ end
106
143
 
107
- def same_line?(token1, token2)
108
- token1.line == token2.line
144
+ add_offense(range, message: MSG_SPACE) do |corrector|
145
+ corrector.insert_before(range, ' ')
146
+ end
109
147
  end
110
148
 
111
149
  def parens?(token1, token2)
112
150
  token1.left_parens? || token2.right_parens?
113
151
  end
114
152
 
153
+ def left_parens?(token1, token2)
154
+ token1.left_parens? && token2.left_parens?
155
+ end
156
+
157
+ def right_parens?(token1, token2)
158
+ token1.right_parens? && token2.right_parens?
159
+ end
160
+
115
161
  def can_be_ignored?(token1, token2)
116
162
  return true unless parens?(token1, token2)
117
163
 
@@ -107,7 +107,7 @@ module RuboCop
107
107
  current_token = tokens.reverse.find(&:left_ref_bracket?)
108
108
  previous_token = previous_token(current_token)
109
109
 
110
- if node.method?(:[]=) || previous_token && !previous_token.right_bracket?
110
+ if node.method?(:[]=) || (previous_token && !previous_token.right_bracket?)
111
111
  tokens.find(&:left_ref_bracket?)
112
112
  else
113
113
  current_token
@@ -41,6 +41,7 @@ module RuboCop
41
41
  #
42
42
  class TrailingWhitespace < Base
43
43
  include RangeHelp
44
+ include Heredoc
44
45
  extend AutoCorrector
45
46
 
46
47
  MSG = 'Trailing whitespace detected.'
@@ -54,6 +55,8 @@ module RuboCop
54
55
  end
55
56
  end
56
57
 
58
+ def on_heredoc(_node); end
59
+
57
60
  private
58
61
 
59
62
  def process_line(line, lineno)
@@ -63,13 +66,33 @@ module RuboCop
63
66
  range = offense_range(lineno, line)
64
67
  add_offense(range) do |corrector|
65
68
  if heredoc
66
- corrector.wrap(range, "\#{'", "'}") unless static?(heredoc)
69
+ process_line_in_heredoc(corrector, range, heredoc)
67
70
  else
68
71
  corrector.remove(range)
69
72
  end
70
73
  end
71
74
  end
72
75
 
76
+ def process_line_in_heredoc(corrector, range, heredoc)
77
+ indent_level = indent_level(find_heredoc(range.line).loc.heredoc_body.source)
78
+ whitespace_only = whitespace_only?(range)
79
+ if whitespace_only && whitespace_is_indentation?(range, indent_level)
80
+ corrector.remove(range)
81
+ elsif !static?(heredoc)
82
+ range = range_between(range.begin_pos + indent_level, range.end_pos) if whitespace_only
83
+ corrector.wrap(range, "\#{'", "'}")
84
+ end
85
+ end
86
+
87
+ def whitespace_is_indentation?(range, level)
88
+ range.source[/ +/].length <= level
89
+ end
90
+
91
+ def whitespace_only?(range)
92
+ source = range_with_surrounding_space(range: range).source
93
+ source.start_with?("\n") && source.end_with?("\n")
94
+ end
95
+
73
96
  def static?(heredoc)
74
97
  heredoc.loc.expression.source.end_with? "'"
75
98
  end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # This cop looks for expressions containing multiple binary operators
7
+ # where precedence is ambiguous due to lack of parentheses. For example,
8
+ # in `1 + 2 * 3`, the multiplication will happen before the addition, but
9
+ # lexically it appears that the addition will happen first.
10
+ #
11
+ # The cop does not consider unary operators (ie. `!a` or `-b`) or comparison
12
+ # operators (ie. `a =~ b`) because those are not ambiguous.
13
+ #
14
+ # NOTE: Ranges are handled by `Lint/AmbiguousRange`.
15
+ #
16
+ # @example
17
+ # # bad
18
+ # a + b * c
19
+ # a || b && c
20
+ # a ** b + c
21
+ #
22
+ # # good (different precedence)
23
+ # a + (b * c)
24
+ # a || (b && c)
25
+ # (a ** b) + c
26
+ #
27
+ # # good (same precedence)
28
+ # a + b + c
29
+ # a * b / c % d
30
+ class AmbiguousOperatorPrecedence < Base
31
+ extend AutoCorrector
32
+
33
+ # See https://ruby-doc.org/core-3.0.2/doc/syntax/precedence_rdoc.html
34
+ PRECEDENCE = [
35
+ %i[**],
36
+ %i[* / %],
37
+ %i[+ -],
38
+ %i[<< >>],
39
+ %i[&],
40
+ %i[| ^],
41
+ %i[&&],
42
+ %i[||]
43
+ ].freeze
44
+ RESTRICT_ON_SEND = PRECEDENCE.flatten.freeze
45
+ MSG = 'Wrap expressions with varying precedence with parentheses to avoid ambiguity.'
46
+
47
+ def on_new_investigation
48
+ # Cache the precedence of each node being investigated
49
+ # so that we only need to calculate it once
50
+ @node_precedences = {}
51
+ super
52
+ end
53
+
54
+ def on_and(node)
55
+ return unless (parent = node.parent)
56
+
57
+ return if parent.begin_type? # if the `and` is in a `begin`, it's parenthesized already
58
+ return unless parent.or_type?
59
+
60
+ add_offense(node) do |corrector|
61
+ autocorrect(corrector, node)
62
+ end
63
+ end
64
+
65
+ def on_send(node)
66
+ return if node.parenthesized?
67
+
68
+ return unless (parent = node.parent)
69
+ return unless operator?(parent)
70
+ return unless greater_precedence?(node, parent)
71
+
72
+ add_offense(node) do |corrector|
73
+ autocorrect(corrector, node)
74
+ end
75
+ end
76
+
77
+ private
78
+
79
+ def precedence(node)
80
+ @node_precedences.fetch(node) do
81
+ PRECEDENCE.index { |operators| operators.include?(operator_name(node)) }
82
+ end
83
+ end
84
+
85
+ def operator?(node)
86
+ (node.send_type? && RESTRICT_ON_SEND.include?(node.method_name)) || node.operator_keyword?
87
+ end
88
+
89
+ def greater_precedence?(node1, node2)
90
+ node1_precedence = precedence(node1)
91
+ node2_precedence = precedence(node2)
92
+ return false unless node1_precedence && node2_precedence
93
+
94
+ node2_precedence > node1_precedence
95
+ end
96
+
97
+ def operator_name(node)
98
+ if node.send_type?
99
+ node.method_name
100
+ else
101
+ node.operator.to_sym
102
+ end
103
+ end
104
+
105
+ def autocorrect(corrector, node)
106
+ corrector.wrap(node, '(', ')')
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end