rubocop 1.50.2 → 1.62.1

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 (339) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +7 -5
  4. data/assets/output.css.erb +159 -0
  5. data/assets/output.html.erb +1 -160
  6. data/config/default.yml +196 -28
  7. data/config/obsoletion.yml +5 -0
  8. data/lib/rubocop/cli/command/auto_generate_config.rb +22 -8
  9. data/lib/rubocop/cli/command/lsp.rb +19 -0
  10. data/lib/rubocop/cli.rb +10 -2
  11. data/lib/rubocop/config.rb +8 -2
  12. data/lib/rubocop/config_finder.rb +14 -4
  13. data/lib/rubocop/config_loader.rb +0 -1
  14. data/lib/rubocop/config_loader_resolver.rb +4 -3
  15. data/lib/rubocop/config_obsoletion/parameter_rule.rb +9 -1
  16. data/lib/rubocop/config_obsoletion.rb +13 -10
  17. data/lib/rubocop/config_validator.rb +14 -7
  18. data/lib/rubocop/cop/autocorrect_logic.rb +9 -2
  19. data/lib/rubocop/cop/base.rb +23 -4
  20. data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -0
  21. data/lib/rubocop/cop/bundler/duplicated_group.rb +127 -0
  22. data/lib/rubocop/cop/bundler/gem_comment.rb +3 -3
  23. data/lib/rubocop/cop/bundler/gem_version.rb +2 -2
  24. data/lib/rubocop/cop/bundler/ordered_gems.rb +9 -1
  25. data/lib/rubocop/cop/correctors/alignment_corrector.rb +1 -1
  26. data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +4 -8
  27. data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +5 -13
  28. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +7 -4
  29. data/lib/rubocop/cop/exclude_limit.rb +1 -1
  30. data/lib/rubocop/cop/gemspec/dependency_version.rb +2 -2
  31. data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +2 -2
  32. data/lib/rubocop/cop/gemspec/development_dependencies.rb +1 -1
  33. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +9 -1
  34. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +5 -1
  35. data/lib/rubocop/cop/generator/require_file_injector.rb +1 -1
  36. data/lib/rubocop/cop/internal_affairs/cop_description.rb +32 -8
  37. data/lib/rubocop/cop/internal_affairs/example_description.rb +45 -24
  38. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +3 -1
  39. data/lib/rubocop/cop/internal_affairs/method_name_end_with.rb +8 -6
  40. data/lib/rubocop/cop/internal_affairs/method_name_equal.rb +19 -20
  41. data/lib/rubocop/cop/internal_affairs/node_first_or_last_argument.rb +53 -0
  42. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +127 -33
  43. data/lib/rubocop/cop/internal_affairs/redundant_expect_offense_arguments.rb +34 -0
  44. data/lib/rubocop/cop/internal_affairs/redundant_method_dispatch_node.rb +11 -2
  45. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +2 -0
  46. data/lib/rubocop/cop/internal_affairs.rb +2 -0
  47. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
  48. data/lib/rubocop/cop/layout/class_structure.rb +7 -0
  49. data/lib/rubocop/cop/layout/closing_heredoc_indentation.rb +1 -2
  50. data/lib/rubocop/cop/layout/dot_position.rb +1 -5
  51. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +42 -9
  52. data/lib/rubocop/cop/layout/empty_line_after_magic_comment.rb +14 -7
  53. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +27 -4
  54. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +2 -0
  55. data/lib/rubocop/cop/layout/end_alignment.rb +15 -3
  56. data/lib/rubocop/cop/layout/extra_spacing.rb +4 -10
  57. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +22 -7
  58. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +1 -1
  59. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +4 -4
  60. data/lib/rubocop/cop/layout/heredoc_indentation.rb +4 -1
  61. data/lib/rubocop/cop/layout/indentation_style.rb +1 -1
  62. data/lib/rubocop/cop/layout/indentation_width.rb +3 -3
  63. data/lib/rubocop/cop/layout/leading_comment_space.rb +1 -1
  64. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +17 -9
  65. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
  66. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +2 -0
  67. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +18 -3
  68. data/lib/rubocop/cop/layout/redundant_line_break.rb +30 -7
  69. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +4 -4
  70. data/lib/rubocop/cop/layout/single_line_block_chain.rb +5 -0
  71. data/lib/rubocop/cop/layout/space_after_comma.rb +9 -1
  72. data/lib/rubocop/cop/layout/space_after_not.rb +1 -1
  73. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +2 -2
  74. data/lib/rubocop/cop/layout/space_around_operators.rb +53 -21
  75. data/lib/rubocop/cop/layout/space_before_block_braces.rb +19 -10
  76. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +2 -0
  77. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +1 -1
  78. data/lib/rubocop/cop/layout/space_inside_parens.rb +1 -1
  79. data/lib/rubocop/cop/layout/space_inside_range_literal.rb +1 -1
  80. data/lib/rubocop/cop/layout/trailing_empty_lines.rb +5 -0
  81. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +13 -1
  82. data/lib/rubocop/cop/lint/assignment_in_condition.rb +4 -4
  83. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +2 -2
  84. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +1 -1
  85. data/lib/rubocop/cop/lint/debugger.rb +19 -5
  86. data/lib/rubocop/cop/lint/duplicate_hash_key.rb +2 -1
  87. data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -1
  88. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +46 -19
  89. data/lib/rubocop/cop/lint/empty_block.rb +1 -1
  90. data/lib/rubocop/cop/lint/empty_conditional_body.rb +1 -1
  91. data/lib/rubocop/cop/lint/erb_new_arguments.rb +6 -7
  92. data/lib/rubocop/cop/lint/float_comparison.rb +10 -0
  93. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +2 -1
  94. data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +1 -1
  95. data/lib/rubocop/cop/lint/identity_comparison.rb +0 -1
  96. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +5 -3
  97. data/lib/rubocop/cop/lint/inherit_exception.rb +9 -0
  98. data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +56 -0
  99. data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +1 -1
  100. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +85 -0
  101. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
  102. data/lib/rubocop/cop/lint/missing_super.rb +34 -5
  103. data/lib/rubocop/cop/lint/mixed_case_range.rb +111 -0
  104. data/lib/rubocop/cop/lint/next_without_accumulator.rb +6 -21
  105. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +10 -7
  106. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +3 -5
  107. data/lib/rubocop/cop/lint/number_conversion.rb +14 -4
  108. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +2 -2
  109. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +0 -1
  110. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +2 -2
  111. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +130 -0
  112. data/lib/rubocop/cop/lint/redundant_require_statement.rb +12 -3
  113. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +72 -8
  114. data/lib/rubocop/cop/lint/redundant_string_coercion.rb +1 -1
  115. data/lib/rubocop/cop/lint/redundant_with_index.rb +3 -2
  116. data/lib/rubocop/cop/lint/redundant_with_object.rb +2 -2
  117. data/lib/rubocop/cop/lint/rescue_type.rb +1 -3
  118. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +14 -8
  119. data/lib/rubocop/cop/lint/script_permission.rb +3 -3
  120. data/lib/rubocop/cop/lint/self_assignment.rb +38 -0
  121. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +1 -2
  122. data/lib/rubocop/cop/lint/shadowed_argument.rb +1 -0
  123. data/lib/rubocop/cop/lint/shadowed_exception.rb +5 -11
  124. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +7 -1
  125. data/lib/rubocop/cop/lint/struct_new_override.rb +12 -12
  126. data/lib/rubocop/cop/lint/suppressed_exception.rb +2 -2
  127. data/lib/rubocop/cop/lint/symbol_conversion.rb +8 -3
  128. data/lib/rubocop/cop/lint/syntax.rb +6 -3
  129. data/lib/rubocop/cop/lint/to_enum_arguments.rb +12 -5
  130. data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +23 -9
  131. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -1
  132. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +2 -2
  133. data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
  134. data/lib/rubocop/cop/lint/useless_assignment.rb +94 -10
  135. data/lib/rubocop/cop/lint/useless_times.rb +2 -2
  136. data/lib/rubocop/cop/lint/void.rb +97 -11
  137. data/lib/rubocop/cop/metrics/abc_size.rb +3 -3
  138. data/lib/rubocop/cop/metrics/block_length.rb +1 -1
  139. data/lib/rubocop/cop/metrics/class_length.rb +8 -3
  140. data/lib/rubocop/cop/metrics/method_length.rb +1 -1
  141. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +1 -2
  142. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +32 -4
  143. data/lib/rubocop/cop/migration/department_name.rb +2 -2
  144. data/lib/rubocop/cop/mixin/allowed_receivers.rb +34 -0
  145. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  146. data/lib/rubocop/cop/mixin/comments_help.rb +19 -11
  147. data/lib/rubocop/cop/mixin/configurable_formatting.rb +1 -0
  148. data/lib/rubocop/cop/mixin/def_node.rb +1 -1
  149. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -1
  150. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +14 -11
  151. data/lib/rubocop/cop/mixin/heredoc.rb +6 -2
  152. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +4 -3
  153. data/lib/rubocop/cop/mixin/percent_literal.rb +1 -1
  154. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +6 -8
  155. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +1 -1
  156. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  157. data/lib/rubocop/cop/mixin/string_help.rb +4 -2
  158. data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
  159. data/lib/rubocop/cop/naming/block_forwarding.rb +13 -5
  160. data/lib/rubocop/cop/naming/constant_name.rb +2 -3
  161. data/lib/rubocop/cop/naming/file_name.rb +1 -1
  162. data/lib/rubocop/cop/naming/heredoc_delimiter_naming.rb +3 -1
  163. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +26 -11
  164. data/lib/rubocop/cop/naming/predicate_name.rb +2 -2
  165. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +11 -3
  166. data/lib/rubocop/cop/naming/variable_name.rb +6 -1
  167. data/lib/rubocop/cop/registry.rb +1 -1
  168. data/lib/rubocop/cop/security/open.rb +2 -2
  169. data/lib/rubocop/cop/style/access_modifier_declarations.rb +2 -2
  170. data/lib/rubocop/cop/style/accessor_grouping.rb +6 -2
  171. data/lib/rubocop/cop/style/alias.rb +9 -8
  172. data/lib/rubocop/cop/style/arguments_forwarding.rb +411 -63
  173. data/lib/rubocop/cop/style/array_first_last.rb +64 -0
  174. data/lib/rubocop/cop/style/array_intersect.rb +13 -5
  175. data/lib/rubocop/cop/style/attr.rb +11 -1
  176. data/lib/rubocop/cop/style/auto_resource_cleanup.rb +21 -14
  177. data/lib/rubocop/cop/style/begin_block.rb +1 -2
  178. data/lib/rubocop/cop/style/bisected_attr_accessor.rb +2 -2
  179. data/lib/rubocop/cop/style/block_comments.rb +1 -1
  180. data/lib/rubocop/cop/style/block_delimiters.rb +5 -4
  181. data/lib/rubocop/cop/style/case_like_if.rb +5 -5
  182. data/lib/rubocop/cop/style/class_and_module_children.rb +1 -1
  183. data/lib/rubocop/cop/style/class_check.rb +1 -0
  184. data/lib/rubocop/cop/style/class_equality_comparison.rb +24 -39
  185. data/lib/rubocop/cop/style/class_vars.rb +3 -3
  186. data/lib/rubocop/cop/style/collection_compact.rb +32 -12
  187. data/lib/rubocop/cop/style/collection_methods.rb +2 -0
  188. data/lib/rubocop/cop/style/colon_method_call.rb +2 -2
  189. data/lib/rubocop/cop/style/combinable_loops.rb +36 -8
  190. data/lib/rubocop/cop/style/commented_keyword.rb +5 -2
  191. data/lib/rubocop/cop/style/concat_array_literals.rb +2 -1
  192. data/lib/rubocop/cop/style/conditional_assignment.rb +11 -10
  193. data/lib/rubocop/cop/style/copyright.rb +5 -2
  194. data/lib/rubocop/cop/style/date_time.rb +5 -4
  195. data/lib/rubocop/cop/style/dir.rb +1 -1
  196. data/lib/rubocop/cop/style/dir_empty.rb +8 -14
  197. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +1 -1
  198. data/lib/rubocop/cop/style/documentation.rb +1 -1
  199. data/lib/rubocop/cop/style/each_for_simple_loop.rb +7 -7
  200. data/lib/rubocop/cop/style/each_with_object.rb +2 -2
  201. data/lib/rubocop/cop/style/empty_case_condition.rb +6 -1
  202. data/lib/rubocop/cop/style/empty_literal.rb +1 -1
  203. data/lib/rubocop/cop/style/eval_with_location.rb +8 -19
  204. data/lib/rubocop/cop/style/exact_regexp_match.rb +69 -0
  205. data/lib/rubocop/cop/style/explicit_block_argument.rb +2 -2
  206. data/lib/rubocop/cop/style/file_read.rb +2 -2
  207. data/lib/rubocop/cop/style/for.rb +3 -1
  208. data/lib/rubocop/cop/style/format_string.rb +24 -3
  209. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +3 -1
  210. data/lib/rubocop/cop/style/guard_clause.rb +28 -0
  211. data/lib/rubocop/cop/style/hash_conversion.rb +10 -0
  212. data/lib/rubocop/cop/style/hash_each_methods.rb +106 -33
  213. data/lib/rubocop/cop/style/hash_except.rb +21 -9
  214. data/lib/rubocop/cop/style/hash_syntax.rb +6 -2
  215. data/lib/rubocop/cop/style/hash_transform_keys.rb +2 -2
  216. data/lib/rubocop/cop/style/hash_transform_values.rb +2 -2
  217. data/lib/rubocop/cop/style/identical_conditional_branches.rb +34 -5
  218. data/lib/rubocop/cop/style/if_inside_else.rb +6 -0
  219. data/lib/rubocop/cop/style/if_unless_modifier.rb +3 -0
  220. data/lib/rubocop/cop/style/if_with_semicolon.rb +2 -2
  221. data/lib/rubocop/cop/style/inverse_methods.rb +14 -13
  222. data/lib/rubocop/cop/style/invertible_unless_condition.rb +54 -8
  223. data/lib/rubocop/cop/style/lambda.rb +3 -3
  224. data/lib/rubocop/cop/style/lambda_call.rb +5 -0
  225. data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +8 -10
  226. data/lib/rubocop/cop/style/map_to_hash.rb +17 -7
  227. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +24 -9
  228. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +2 -4
  229. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +20 -0
  230. data/lib/rubocop/cop/style/method_def_parentheses.rb +1 -1
  231. data/lib/rubocop/cop/style/missing_respond_to_missing.rb +2 -2
  232. data/lib/rubocop/cop/style/mixin_grouping.rb +1 -1
  233. data/lib/rubocop/cop/style/multiline_block_chain.rb +1 -1
  234. data/lib/rubocop/cop/style/multiline_method_signature.rb +10 -1
  235. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +6 -4
  236. data/lib/rubocop/cop/style/multiple_comparison.rb +14 -0
  237. data/lib/rubocop/cop/style/nested_ternary_operator.rb +3 -11
  238. data/lib/rubocop/cop/style/next.rb +1 -1
  239. data/lib/rubocop/cop/style/nil_comparison.rb +2 -0
  240. data/lib/rubocop/cop/style/numeric_literal_prefix.rb +1 -1
  241. data/lib/rubocop/cop/style/numeric_literals.rb +1 -1
  242. data/lib/rubocop/cop/style/object_then.rb +5 -3
  243. data/lib/rubocop/cop/style/open_struct_use.rb +1 -1
  244. data/lib/rubocop/cop/style/operator_method_call.rb +8 -2
  245. data/lib/rubocop/cop/style/parallel_assignment.rb +3 -5
  246. data/lib/rubocop/cop/style/parentheses_around_condition.rb +8 -0
  247. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +1 -1
  248. data/lib/rubocop/cop/style/preferred_hash_methods.rb +1 -1
  249. data/lib/rubocop/cop/style/raise_args.rb +4 -1
  250. data/lib/rubocop/cop/style/redundant_argument.rb +10 -4
  251. data/lib/rubocop/cop/style/redundant_array_constructor.rb +77 -0
  252. data/lib/rubocop/cop/style/redundant_assignment.rb +10 -2
  253. data/lib/rubocop/cop/style/redundant_begin.rb +10 -2
  254. data/lib/rubocop/cop/style/redundant_conditional.rb +2 -10
  255. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +39 -0
  256. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +93 -5
  257. data/lib/rubocop/cop/style/redundant_each.rb +7 -4
  258. data/lib/rubocop/cop/style/redundant_exception.rb +32 -12
  259. data/lib/rubocop/cop/style/redundant_fetch_block.rb +3 -3
  260. data/lib/rubocop/cop/style/redundant_filter_chain.rb +118 -0
  261. data/lib/rubocop/cop/style/redundant_line_continuation.rb +32 -8
  262. data/lib/rubocop/cop/style/redundant_parentheses.rb +72 -23
  263. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +100 -0
  264. data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +46 -0
  265. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +2 -1
  266. data/lib/rubocop/cop/style/redundant_return.rb +14 -3
  267. data/lib/rubocop/cop/style/redundant_self.rb +17 -2
  268. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +8 -1
  269. data/lib/rubocop/cop/style/redundant_sort.rb +10 -9
  270. data/lib/rubocop/cop/style/redundant_sort_by.rb +2 -2
  271. data/lib/rubocop/cop/style/redundant_string_escape.rb +3 -1
  272. data/lib/rubocop/cop/style/regexp_literal.rb +11 -2
  273. data/lib/rubocop/cop/style/require_order.rb +11 -5
  274. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -3
  275. data/lib/rubocop/cop/style/return_nil.rb +6 -2
  276. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +95 -0
  277. data/lib/rubocop/cop/style/sample.rb +3 -4
  278. data/lib/rubocop/cop/style/select_by_regexp.rb +22 -11
  279. data/lib/rubocop/cop/style/self_assignment.rb +1 -1
  280. data/lib/rubocop/cop/style/semicolon.rb +20 -4
  281. data/lib/rubocop/cop/style/signal_exception.rb +1 -1
  282. data/lib/rubocop/cop/style/single_argument_dig.rb +7 -3
  283. data/lib/rubocop/cop/style/single_line_do_end_block.rb +67 -0
  284. data/lib/rubocop/cop/style/single_line_methods.rb +1 -1
  285. data/lib/rubocop/cop/style/slicing_with_range.rb +76 -10
  286. data/lib/rubocop/cop/style/sole_nested_conditional.rb +6 -2
  287. data/lib/rubocop/cop/style/special_global_vars.rb +3 -4
  288. data/lib/rubocop/cop/style/string_chars.rb +1 -0
  289. data/lib/rubocop/cop/style/string_literals_in_interpolation.rb +30 -5
  290. data/lib/rubocop/cop/style/strip.rb +7 -4
  291. data/lib/rubocop/cop/style/super_with_args_parentheses.rb +35 -0
  292. data/lib/rubocop/cop/style/symbol_array.rb +35 -15
  293. data/lib/rubocop/cop/style/symbol_proc.rb +36 -0
  294. data/lib/rubocop/cop/style/unpack_first.rb +11 -14
  295. data/lib/rubocop/cop/style/yaml_file_read.rb +66 -0
  296. data/lib/rubocop/cop/style/yoda_condition.rb +4 -2
  297. data/lib/rubocop/cop/style/yoda_expression.rb +8 -7
  298. data/lib/rubocop/cop/team.rb +1 -1
  299. data/lib/rubocop/cop/util.rb +1 -1
  300. data/lib/rubocop/cop/utils/regexp_ranges.rb +113 -0
  301. data/lib/rubocop/cop/variable_force/assignment.rb +45 -4
  302. data/lib/rubocop/cop/variable_force/variable_table.rb +2 -2
  303. data/lib/rubocop/cop/variable_force.rb +1 -0
  304. data/lib/rubocop/cops_documentation_generator.rb +16 -4
  305. data/lib/rubocop/directive_comment.rb +10 -8
  306. data/lib/rubocop/ext/regexp_node.rb +9 -4
  307. data/lib/rubocop/ext/regexp_parser.rb +4 -1
  308. data/lib/rubocop/file_finder.rb +4 -7
  309. data/lib/rubocop/formatter/disabled_config_formatter.rb +17 -6
  310. data/lib/rubocop/formatter/html_formatter.rb +35 -14
  311. data/lib/rubocop/formatter/json_formatter.rb +0 -1
  312. data/lib/rubocop/formatter/junit_formatter.rb +1 -1
  313. data/lib/rubocop/formatter/offense_count_formatter.rb +12 -2
  314. data/lib/rubocop/formatter.rb +1 -1
  315. data/lib/rubocop/lsp/logger.rb +22 -0
  316. data/lib/rubocop/lsp/routes.rb +246 -0
  317. data/lib/rubocop/lsp/runtime.rb +99 -0
  318. data/lib/rubocop/lsp/server.rb +71 -0
  319. data/lib/rubocop/lsp/severity.rb +27 -0
  320. data/lib/rubocop/lsp.rb +29 -0
  321. data/lib/rubocop/magic_comment.rb +13 -11
  322. data/lib/rubocop/options.rb +22 -9
  323. data/lib/rubocop/path_util.rb +6 -2
  324. data/lib/rubocop/result_cache.rb +5 -2
  325. data/lib/rubocop/rspec/cop_helper.rb +8 -2
  326. data/lib/rubocop/rspec/expect_offense.rb +8 -8
  327. data/lib/rubocop/rspec/shared_contexts.rb +42 -18
  328. data/lib/rubocop/rspec/support.rb +2 -0
  329. data/lib/rubocop/runner.rb +15 -6
  330. data/lib/rubocop/server/cache.rb +1 -1
  331. data/lib/rubocop/server/client_command/exec.rb +3 -3
  332. data/lib/rubocop/server/server_command/exec.rb +0 -1
  333. data/lib/rubocop/string_interpreter.rb +3 -3
  334. data/lib/rubocop/target_finder.rb +91 -81
  335. data/lib/rubocop/target_ruby.rb +85 -78
  336. data/lib/rubocop/version.rb +27 -8
  337. data/lib/rubocop.rb +19 -0
  338. metadata +56 -14
  339. /data/lib/rubocop/formatter/{git_hub_actions_formatter.rb → github_actions_formatter.rb} +0 -0
@@ -17,10 +17,16 @@ module RuboCop
17
17
  # @example
18
18
  # # bad
19
19
  # hash.keys.each { |k| p k }
20
- # hash.values.each { |v| p v }
20
+ # hash.each { |k, unused_value| p k }
21
21
  #
22
22
  # # good
23
23
  # hash.each_key { |k| p k }
24
+ #
25
+ # # bad
26
+ # hash.values.each { |v| p v }
27
+ # hash.each { |unused_key, v| p v }
28
+ #
29
+ # # good
24
30
  # hash.each_value { |v| p v }
25
31
  #
26
32
  # @example AllowedReceivers: ['execute']
@@ -28,29 +34,64 @@ module RuboCop
28
34
  # execute(sql).keys.each { |v| p v }
29
35
  # execute(sql).values.each { |v| p v }
30
36
  class HashEachMethods < Base
37
+ include AllowedReceivers
31
38
  include Lint::UnusedArgument
32
39
  extend AutoCorrector
33
40
 
34
41
  MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
42
+ UNUSED_BLOCK_ARG_MSG = "#{MSG.chop} and remove the unused `%<unused_code>s` block argument."
43
+ ARRAY_CONVERTER_METHODS = %i[assoc chunk flatten rassoc sort sort_by to_a].freeze
35
44
 
36
45
  # @!method kv_each(node)
37
46
  def_node_matcher :kv_each, <<~PATTERN
38
- ({block numblock} $(send (send _ ${:keys :values}) :each) ...)
47
+ ({block numblock} $(call (call _ ${:keys :values}) :each) ...)
48
+ PATTERN
49
+
50
+ # @!method each_arguments(node)
51
+ def_node_matcher :each_arguments, <<~PATTERN
52
+ (block (call _ :each)(args $_key $_value) ...)
39
53
  PATTERN
40
54
 
41
55
  # @!method kv_each_with_block_pass(node)
42
56
  def_node_matcher :kv_each_with_block_pass, <<~PATTERN
43
- (send $(send _ ${:keys :values}) :each (block_pass (sym _)))
57
+ (call $(call _ ${:keys :values}) :each (block_pass (sym _)))
44
58
  PATTERN
45
59
 
46
60
  def on_block(node)
61
+ return unless handleable?(node)
62
+
47
63
  kv_each(node) do |target, method|
48
- register_kv_offense(target, method)
64
+ register_kv_offense(target, method) and return
49
65
  end
50
- end
51
66
 
67
+ return unless (key, value = each_arguments(node))
68
+
69
+ check_unused_block_args(node, key, value)
70
+ end
52
71
  alias on_numblock on_block
53
72
 
73
+ # rubocop:disable Metrics/AbcSize
74
+ def check_unused_block_args(node, key, value)
75
+ return if node.body.nil?
76
+
77
+ value_unused = unused_block_arg_exist?(node, value)
78
+ key_unused = unused_block_arg_exist?(node, key)
79
+ return if value_unused && key_unused
80
+
81
+ if value_unused
82
+ message = message('each_key', node.method_name, value.source)
83
+ unused_range = key.source_range.end.join(value.source_range.end)
84
+
85
+ register_each_args_offense(node, message, 'each_key', unused_range)
86
+ elsif key_unused
87
+ message = message('each_value', node.method_name, key.source)
88
+ unused_range = key.source_range.begin.join(value.source_range.begin)
89
+
90
+ register_each_args_offense(node, message, 'each_value', unused_range)
91
+ end
92
+ end
93
+ # rubocop:enable Metrics/AbcSize
94
+
54
95
  def on_block_pass(node)
55
96
  kv_each_with_block_pass(node.parent) do |target, method|
56
97
  register_kv_with_block_pass_offense(node, target, method)
@@ -59,27 +100,81 @@ module RuboCop
59
100
 
60
101
  private
61
102
 
103
+ def handleable?(node)
104
+ return false if use_array_converter_method_as_preceding?(node)
105
+ return false unless (root_receiver = root_receiver(node))
106
+
107
+ !root_receiver.literal? || root_receiver.hash_type?
108
+ end
109
+
62
110
  def register_kv_offense(target, method)
63
111
  return unless (parent_receiver = target.receiver.receiver)
64
112
  return if allowed_receiver?(parent_receiver)
65
113
 
66
- add_offense(kv_range(target), message: format_message(method)) do |corrector|
114
+ current = target.receiver.loc.selector.join(target.source_range.end).source
115
+
116
+ add_offense(kv_range(target), message: format_message(method, current)) do |corrector|
67
117
  correct_key_value_each(target, corrector)
68
118
  end
69
119
  end
70
120
 
121
+ def unused_block_arg_exist?(node, block_arg)
122
+ lvar_sources = node.body.each_descendant(:lvar).map(&:source)
123
+
124
+ if block_arg.mlhs_type?
125
+ block_arg.each_descendant(:arg, :restarg).all? do |block_arg|
126
+ lvar_sources.none?(block_arg.source.delete_prefix('*'))
127
+ end
128
+ else
129
+ lvar_sources.none?(block_arg.source.delete_prefix('*'))
130
+ end
131
+ end
132
+
133
+ def message(prefer, method_name, unused_code)
134
+ format(
135
+ UNUSED_BLOCK_ARG_MSG, prefer: prefer, current: method_name, unused_code: unused_code
136
+ )
137
+ end
138
+
139
+ def register_each_args_offense(node, message, prefer, unused_range)
140
+ add_offense(node, message: message) do |corrector|
141
+ corrector.replace(node.send_node.loc.selector, prefer)
142
+ corrector.remove(unused_range)
143
+ end
144
+ end
145
+
71
146
  def register_kv_with_block_pass_offense(node, target, method)
72
147
  return unless (parent_receiver = node.parent.receiver.receiver)
73
148
  return if allowed_receiver?(parent_receiver)
74
149
 
75
- range = target.loc.selector.with(end_pos: node.parent.loc.selector.end_pos)
76
- add_offense(range, message: format_message(method)) do |corrector|
150
+ range = target.loc.selector.join(node.parent.loc.selector.end)
151
+
152
+ add_offense(range, message: format_message(method, range.source)) do |corrector|
77
153
  corrector.replace(range, "each_#{method[0..-2]}")
78
154
  end
79
155
  end
80
156
 
81
- def format_message(method_name)
82
- format(MSG, prefer: "each_#{method_name[0..-2]}", current: "#{method_name}.each")
157
+ def use_array_converter_method_as_preceding?(node)
158
+ return false unless (preceding_method = node.children.first.children.first)
159
+ unless preceding_method.call_type? ||
160
+ preceding_method.block_type? || preceding_method.numblock_type?
161
+ return false
162
+ end
163
+
164
+ ARRAY_CONVERTER_METHODS.include?(preceding_method.method_name)
165
+ end
166
+
167
+ def root_receiver(node)
168
+ receiver = node.receiver
169
+ if receiver&.receiver
170
+ root_receiver(receiver)
171
+ else
172
+ receiver
173
+ end
174
+ end
175
+
176
+ def format_message(method_name, current)
177
+ format(MSG, prefer: "each_#{method_name[0..-2]}", current: current)
83
178
  end
84
179
 
85
180
  def check_argument(variable)
@@ -102,7 +197,7 @@ module RuboCop
102
197
  name = "each_#{node.receiver.method_name.to_s.chop}"
103
198
  return correct_implicit(node, corrector, name) unless receiver
104
199
 
105
- new_source = receiver.source + ".#{name}"
200
+ new_source = receiver.source + "#{node.loc.dot.source}#{name}"
106
201
  corrector.replace(node, new_source)
107
202
  end
108
203
 
@@ -116,28 +211,6 @@ module RuboCop
116
211
  def kv_range(outer_node)
117
212
  outer_node.receiver.loc.selector.join(outer_node.loc.selector)
118
213
  end
119
-
120
- def allowed_receiver?(receiver)
121
- receiver_name = receiver_name(receiver)
122
-
123
- allowed_receivers.include?(receiver_name)
124
- end
125
-
126
- def receiver_name(receiver)
127
- if receiver.send_type?
128
- if receiver.receiver
129
- "#{receiver_name(receiver.receiver)}.#{receiver.method_name}"
130
- else
131
- receiver.method_name.to_s
132
- end
133
- else
134
- receiver.source
135
- end
136
- end
137
-
138
- def allowed_receivers
139
- cop_config.fetch('AllowedReceivers', [])
140
- end
141
214
  end
142
215
  end
143
216
  end
@@ -43,15 +43,15 @@ module RuboCop
43
43
  # @!method bad_method_with_poro?(node)
44
44
  def_node_matcher :bad_method_with_poro?, <<~PATTERN
45
45
  (block
46
- (send _ _)
46
+ (call _ _)
47
47
  (args
48
- (arg _)
48
+ $(arg _)
49
49
  (arg _))
50
50
  {
51
- (send
51
+ $(send
52
52
  _ {:== :!= :eql? :include?} _)
53
53
  (send
54
- (send
54
+ $(send
55
55
  _ {:== :!= :eql? :include?} _) :!)
56
56
  })
57
57
  PATTERN
@@ -61,13 +61,13 @@ module RuboCop
61
61
  (block
62
62
  (send _ _)
63
63
  (args
64
- (arg _)
64
+ $(arg _)
65
65
  (arg _))
66
66
  {
67
- (send
67
+ $(send
68
68
  _ {:== :!= :eql? :in? :include? :exclude?} _)
69
69
  (send
70
- (send
70
+ $(send
71
71
  _ {:== :!= :eql? :in? :include? :exclude?} _) :!)
72
72
  })
73
73
  PATTERN
@@ -86,16 +86,28 @@ module RuboCop
86
86
  corrector.replace(range, preferred_method)
87
87
  end
88
88
  end
89
+ alias on_csend on_send
89
90
 
90
91
  private
91
92
 
93
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
92
94
  def bad_method?(block)
93
95
  if active_support_extensions_enabled?
94
- bad_method_with_active_support?(block)
96
+ bad_method_with_active_support?(block) do |key_arg, send_node|
97
+ if send_node.method?(:in?) && send_node.receiver&.source != key_arg.source
98
+ return false
99
+ end
100
+ return true if !send_node.method?(:include?) && !send_node.method?(:exclude?)
101
+
102
+ send_node.first_argument&.source == key_arg.source
103
+ end
95
104
  else
96
- bad_method_with_poro?(block)
105
+ bad_method_with_poro?(block) do |key_arg, send_node|
106
+ !send_node.method?(:include?) || send_node.first_argument&.source == key_arg.source
107
+ end
97
108
  end
98
109
  end
110
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
99
111
 
100
112
  def semantically_except_method?(send, block)
101
113
  body = block.body
@@ -195,6 +195,7 @@ module RuboCop
195
195
  acceptable_19_syntax_symbol?(pair.key.source)
196
196
  end
197
197
 
198
+ # rubocop:disable Metrics/CyclomaticComplexity
198
199
  def acceptable_19_syntax_symbol?(sym_name)
199
200
  sym_name.delete_prefix!(':')
200
201
 
@@ -209,9 +210,12 @@ module RuboCop
209
210
  # Most hash keys can be matched against a simple regex.
210
211
  return true if /\A[_a-z]\w*[?!]?\z/i.match?(sym_name)
211
212
 
212
- # For more complicated hash keys, let the parser validate the syntax.
213
- parse("{ #{sym_name}: :foo }").valid_syntax?
213
+ return false if target_ruby_version <= 2.1
214
+
215
+ (sym_name.start_with?("'") && sym_name.end_with?("'")) ||
216
+ (sym_name.start_with?('"') && sym_name.end_with?('"'))
214
217
  end
218
+ # rubocop:enable Metrics/CyclomaticComplexity
215
219
 
216
220
  def check(pairs, delim, msg)
217
221
  pairs.each do |pair|
@@ -3,8 +3,8 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Looks for uses of `_.each_with_object({}) {...}`,
7
- # `_.map {...}.to_h`, and `Hash[_.map {...}]` that are actually just
6
+ # Looks for uses of `\_.each_with_object({}) {...}`,
7
+ # `\_.map {...}.to_h`, and `Hash[\_.map {...}]` that are actually just
8
8
  # transforming the keys of a hash, and tries to use a simpler & faster
9
9
  # call to `transform_keys` instead.
10
10
  # It should only be enabled on Ruby version 2.5 or newer.
@@ -3,8 +3,8 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Looks for uses of `_.each_with_object({}) {...}`,
7
- # `_.map {...}.to_h`, and `Hash[_.map {...}]` that are actually just
6
+ # Looks for uses of `\_.each_with_object({}) {...}`,
7
+ # `\_.map {...}.to_h`, and `Hash[\_.map {...}]` that are actually just
8
8
  # transforming the values of a hash, and tries to use a simpler & faster
9
9
  # call to `transform_values` instead.
10
10
  #
@@ -136,7 +136,7 @@ module RuboCop
136
136
 
137
137
  private
138
138
 
139
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
139
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
140
140
  def check_branches(node, branches)
141
141
  # return if any branch is empty. An empty branch can be an `if`
142
142
  # without an `else` or a branch that contains only comments.
@@ -149,22 +149,50 @@ module RuboCop
149
149
  branches.any? { |branch| single_child_branch?(branch) }
150
150
 
151
151
  heads = branches.map { |branch| head(branch) }
152
- check_expressions(node, heads, :before_condition) if duplicated_expressions?(node, heads)
152
+
153
+ return unless duplicated_expressions?(node, heads)
154
+
155
+ condition_variable = assignable_condition_value(node)
156
+
157
+ head = heads.first
158
+ if head.assignment?
159
+ # The `send` node is used instead of the `indexasgn` node, so `name` cannot be used.
160
+ # https://github.com/rubocop/rubocop-ast/blob/v1.29.0/lib/rubocop/ast/node/indexasgn_node.rb
161
+ #
162
+ # FIXME: It would be better to update `RuboCop::AST::OpAsgnNode` or its subclasses to
163
+ # handle `self.foo ||= value` as a solution, instead of using `head.node_parts[0].to_s`.
164
+ assigned_value = head.send_type? ? head.receiver.source : head.node_parts[0].to_s
165
+
166
+ return if condition_variable == assigned_value
167
+ end
168
+
169
+ check_expressions(node, heads, :before_condition)
153
170
  end
154
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
171
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
155
172
 
156
173
  def duplicated_expressions?(node, expressions)
157
174
  unique_expressions = expressions.uniq
158
175
  return false unless expressions.size >= 1 && unique_expressions.one?
159
176
 
160
177
  unique_expression = unique_expressions.first
161
- return true unless unique_expression.assignment?
178
+ return true unless unique_expression&.assignment?
162
179
 
163
180
  lhs = unique_expression.child_nodes.first
164
181
  node.condition.child_nodes.none? { |n| n.source == lhs.source if n.variable? }
165
182
  end
166
183
 
167
- def check_expressions(node, expressions, insert_position) # rubocop:disable Metrics/MethodLength
184
+ def assignable_condition_value(node)
185
+ if node.condition.call_type?
186
+ (receiver = node.condition.receiver) ? receiver.source : node.condition.source
187
+ elsif node.condition.variable?
188
+ node.condition.source
189
+ end
190
+ end
191
+
192
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
193
+ def check_expressions(node, expressions, insert_position)
194
+ return if expressions.any?(&:nil?)
195
+
168
196
  inserted_expression = false
169
197
 
170
198
  expressions.each do |expression|
@@ -184,6 +212,7 @@ module RuboCop
184
212
  end
185
213
  end
186
214
  end
215
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
187
216
 
188
217
  def last_child_of_parent?(node)
189
218
  return true unless (parent = node.parent)
@@ -59,11 +59,13 @@ module RuboCop
59
59
  # end
60
60
  #
61
61
  class IfInsideElse < Base
62
+ include IgnoredNode
62
63
  include RangeHelp
63
64
  extend AutoCorrector
64
65
 
65
66
  MSG = 'Convert `if` nested inside `else` to `elsif`.'
66
67
 
68
+ # rubocop:disable Metrics/CyclomaticComplexity
67
69
  def on_if(node)
68
70
  return if node.ternary? || node.unless?
69
71
 
@@ -73,9 +75,13 @@ module RuboCop
73
75
  return if allow_if_modifier_in_else_branch?(else_branch)
74
76
 
75
77
  add_offense(else_branch.loc.keyword) do |corrector|
78
+ next if part_of_ignored_node?(node)
79
+
76
80
  autocorrect(corrector, else_branch)
81
+ ignore_node(node)
77
82
  end
78
83
  end
84
+ # rubocop:enable Metrics/CyclomaticComplexity
79
85
 
80
86
  private
81
87
 
@@ -84,7 +84,10 @@ module RuboCop
84
84
  return unless (msg = message(node))
85
85
 
86
86
  add_offense(node.loc.keyword, message: format(msg, keyword: node.keyword)) do |corrector|
87
+ next if part_of_ignored_node?(node)
88
+
87
89
  autocorrect(corrector, node)
90
+ ignore_node(node)
88
91
  end
89
92
  end
90
93
 
@@ -47,7 +47,7 @@ module RuboCop
47
47
  def correct_elsif(node)
48
48
  <<~RUBY.chop
49
49
  if #{node.condition.source}
50
- #{node.if_branch.source}
50
+ #{node.if_branch&.source}
51
51
  #{build_else_branch(node.else_branch).chop}
52
52
  end
53
53
  RUBY
@@ -56,7 +56,7 @@ module RuboCop
56
56
  def build_else_branch(second_condition)
57
57
  result = <<~RUBY
58
58
  elsif #{second_condition.condition.source}
59
- #{second_condition.if_branch.source}
59
+ #{second_condition.if_branch&.source}
60
60
  RUBY
61
61
 
62
62
  if second_condition.else_branch
@@ -60,25 +60,25 @@ module RuboCop
60
60
  # @!method inverse_candidate?(node)
61
61
  def_node_matcher :inverse_candidate?, <<~PATTERN
62
62
  {
63
- (send $(send $(...) $_ $...) :!)
64
- (send ({block numblock} $(send $(...) $_) $...) :!)
65
- (send (begin $(send $(...) $_ $...)) :!)
63
+ (send $(call $(...) $_ $...) :!)
64
+ (send ({block numblock} $(call $(...) $_) $...) :!)
65
+ (send (begin $(call $(...) $_ $...)) :!)
66
66
  }
67
67
  PATTERN
68
68
 
69
69
  # @!method inverse_block?(node)
70
70
  def_node_matcher :inverse_block?, <<~PATTERN
71
- ({block numblock} $(send (...) $_) ... { $(send ... :!)
71
+ ({block numblock} $(call (...) $_) ... { $(call ... :!)
72
72
  $(send (...) {:!= :!~} ...)
73
- (begin ... $(send ... :!))
73
+ (begin ... $(call ... :!))
74
74
  (begin ... $(send (...) {:!= :!~} ...))
75
75
  })
76
76
  PATTERN
77
77
 
78
78
  def on_send(node)
79
- inverse_candidate?(node) do |_method_call, lhs, method, rhs|
79
+ inverse_candidate?(node) do |method_call, lhs, method, rhs|
80
80
  return unless inverse_methods.key?(method)
81
- return if negated?(node)
81
+ return if negated?(node) || relational_comparison_with_safe_navigation?(method_call)
82
82
  return if part_of_ignored_node?(node)
83
83
  return if possible_class_hierarchy_check?(lhs, rhs, method)
84
84
 
@@ -87,6 +87,7 @@ module RuboCop
87
87
  end
88
88
  end
89
89
  end
90
+ alias on_csend on_send
90
91
 
91
92
  def on_block(node)
92
93
  inverse_block?(node) do |_method_call, method, block|
@@ -154,16 +155,16 @@ module RuboCop
154
155
  node.parent.respond_to?(:method?) && node.parent.method?(:!)
155
156
  end
156
157
 
158
+ def relational_comparison_with_safe_navigation?(node)
159
+ node.csend_type? && CLASS_COMPARISON_METHODS.include?(node.method_name)
160
+ end
161
+
157
162
  def not_to_receiver(node, method_call)
158
- Parser::Source::Range.new(node.source_range.source_buffer,
159
- node.loc.selector.begin_pos,
160
- method_call.source_range.begin_pos)
163
+ node.loc.selector.begin.join(method_call.source_range.begin)
161
164
  end
162
165
 
163
166
  def end_parentheses(node, method_call)
164
- Parser::Source::Range.new(node.source_range.source_buffer,
165
- method_call.source_range.end_pos,
166
- node.source_range.end_pos)
167
+ method_call.source_range.end.join(node.source_range.end)
167
168
  end
168
169
 
169
170
  # When comparing classes, `!(Integer < Numeric)` is not the same as
@@ -10,12 +10,16 @@ module RuboCop
10
10
  # Methods that can be inverted should be defined in `InverseMethods`. Note that
11
11
  # the relationship of inverse methods needs to be defined in both directions.
12
12
  # For example,
13
- # InverseMethods:
14
- # :!=: :==
15
- # :even?: :odd?
16
- # :odd?: :even?
17
13
  #
18
- # will suggest both `even?` and `odd?` to be inverted, but only `!=` (and not `==`).
14
+ # [source,yaml]
15
+ # ----
16
+ # InverseMethods:
17
+ # :!=: :==
18
+ # :even?: :odd?
19
+ # :odd?: :even?
20
+ # ----
21
+ #
22
+ # will suggest both `even?` and `odd?` to be inverted, but only `!=` (and not `==`).
19
23
  #
20
24
  # @safety
21
25
  # This cop is unsafe because it cannot be guaranteed that the method
@@ -28,12 +32,14 @@ module RuboCop
28
32
  # foo unless x != y
29
33
  # foo unless x >= 10
30
34
  # foo unless x.even?
35
+ # foo unless odd?
31
36
  #
32
37
  # # good
33
38
  # foo if bar
34
39
  # foo if x == y
35
40
  # foo if x < 10
36
41
  # foo if x.odd?
42
+ # foo if even?
37
43
  #
38
44
  # # bad (complex condition)
39
45
  # foo unless x != y || x.even?
@@ -47,7 +53,7 @@ module RuboCop
47
53
  class InvertibleUnlessCondition < Base
48
54
  extend AutoCorrector
49
55
 
50
- MSG = 'Favor `if` with inverted condition over `unless`.'
56
+ MSG = 'Prefer `%<prefer>s` over `%<current>s`.'
51
57
 
52
58
  def on_if(node)
53
59
  return unless node.unless?
@@ -55,7 +61,10 @@ module RuboCop
55
61
  condition = node.condition
56
62
  return unless invertible?(condition)
57
63
 
58
- add_offense(node) do |corrector|
64
+ message = format(MSG, prefer: "#{node.inverse_keyword} #{preferred_condition(condition)}",
65
+ current: "#{node.keyword} #{condition.source}")
66
+
67
+ add_offense(node, message: message) do |corrector|
59
68
  corrector.replace(node.loc.keyword, node.inverse_keyword)
60
69
  autocorrect(corrector, condition)
61
70
  end
@@ -68,7 +77,7 @@ module RuboCop
68
77
  when :begin
69
78
  invertible?(node.children.first)
70
79
  when :send
71
- return if inheritance_check?(node)
80
+ return false if inheritance_check?(node)
72
81
 
73
82
  node.method?(:!) || inverse_methods.key?(node.method_name)
74
83
  when :or, :and
@@ -84,6 +93,43 @@ module RuboCop
84
93
  (argument.const_type? && argument.short_name.to_s.upcase != argument.short_name.to_s)
85
94
  end
86
95
 
96
+ def preferred_condition(node)
97
+ case node.type
98
+ when :begin then "(#{preferred_condition(node.children.first)})"
99
+ when :send then preferred_send_condition(node)
100
+ when :or, :and then preferred_logical_condition(node)
101
+ end
102
+ end
103
+
104
+ def preferred_send_condition(node) # rubocop:disable Metrics/CyclomaticComplexity
105
+ receiver_source = node.receiver&.source
106
+ return receiver_source if node.method?(:!)
107
+
108
+ # receiver may be implicit (self)
109
+ dotted_receiver_source = receiver_source ? "#{receiver_source}." : ''
110
+
111
+ inverse_method_name = inverse_methods[node.method_name]
112
+ return "#{dotted_receiver_source}#{inverse_method_name}" unless node.arguments?
113
+
114
+ argument_list = node.arguments.map(&:source).join(', ')
115
+ if node.operator_method?
116
+ return "#{receiver_source} #{inverse_method_name} #{argument_list}"
117
+ end
118
+
119
+ if node.parenthesized?
120
+ return "#{dotted_receiver_source}#{inverse_method_name}(#{argument_list})"
121
+ end
122
+
123
+ "#{dotted_receiver_source}#{inverse_method_name} #{argument_list}"
124
+ end
125
+
126
+ def preferred_logical_condition(node)
127
+ preferred_lhs = preferred_condition(node.lhs)
128
+ preferred_rhs = preferred_condition(node.rhs)
129
+
130
+ "#{preferred_lhs} #{node.inverse_operator} #{preferred_rhs}"
131
+ end
132
+
87
133
  def autocorrect(corrector, node)
88
134
  case node.type
89
135
  when :begin
@@ -69,10 +69,10 @@ module RuboCop
69
69
  return unless offending_selector?(node, selector)
70
70
 
71
71
  add_offense(node.send_node.source_range, message: message(node, selector)) do |corrector|
72
- if node.send_node.source == 'lambda'
73
- autocorrect_method_to_literal(corrector, node)
74
- else
72
+ if node.send_node.lambda_literal?
75
73
  LambdaLiteralToMethodCorrector.new(node).call(corrector)
74
+ else
75
+ autocorrect_method_to_literal(corrector, node)
76
76
  end
77
77
  end
78
78
  end
@@ -20,6 +20,7 @@ module RuboCop
20
20
  # lambda.(x, y)
21
21
  class LambdaCall < Base
22
22
  include ConfigurableEnforcedStyle
23
+ include IgnoredNode
23
24
  extend AutoCorrector
24
25
 
25
26
  MSG = 'Prefer the use of `%<prefer>s` over `%<current>s`.'
@@ -33,8 +34,12 @@ module RuboCop
33
34
  current = node.source
34
35
 
35
36
  add_offense(node, message: format(MSG, prefer: prefer, current: current)) do |corrector|
37
+ next if part_of_ignored_node?(node)
38
+
36
39
  opposite_style_detected
37
40
  corrector.replace(node, prefer)
41
+
42
+ ignore_node(node)
38
43
  end
39
44
  else
40
45
  correct_style_detected