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
@@ -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