rubocop 1.8.0 → 1.11.0

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 (246) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +14 -13
  3. data/assets/output.html.erb +1 -1
  4. data/config/default.yml +89 -22
  5. data/config/obsoletion.yml +4 -0
  6. data/lib/rubocop.rb +9 -0
  7. data/lib/rubocop/cli/command/auto_genenerate_config.rb +5 -4
  8. data/lib/rubocop/cli/command/execute_runner.rb +1 -1
  9. data/lib/rubocop/cli/command/suggest_extensions.rb +1 -1
  10. data/lib/rubocop/config.rb +5 -2
  11. data/lib/rubocop/config_loader.rb +7 -14
  12. data/lib/rubocop/config_store.rb +12 -1
  13. data/lib/rubocop/cop/base.rb +2 -1
  14. data/lib/rubocop/cop/bundler/duplicated_gem.rb +2 -1
  15. data/lib/rubocop/cop/bundler/gem_comment.rb +1 -0
  16. data/lib/rubocop/cop/bundler/insecure_protocol_source.rb +1 -0
  17. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -0
  18. data/lib/rubocop/cop/exclude_limit.rb +26 -0
  19. data/lib/rubocop/cop/gemspec/date_assignment.rb +57 -0
  20. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +2 -0
  21. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -0
  22. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +2 -0
  23. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +2 -0
  24. data/lib/rubocop/cop/generator.rb +3 -5
  25. data/lib/rubocop/cop/internal_affairs.rb +6 -1
  26. data/lib/rubocop/cop/internal_affairs/empty_line_between_expect_offense_and_correction.rb +68 -0
  27. data/lib/rubocop/cop/internal_affairs/example_description.rb +90 -0
  28. data/lib/rubocop/cop/internal_affairs/method_name_equal.rb +1 -0
  29. data/lib/rubocop/cop/internal_affairs/node_destructuring.rb +2 -0
  30. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +151 -0
  31. data/lib/rubocop/cop/internal_affairs/node_type_predicate.rb +1 -0
  32. data/lib/rubocop/cop/internal_affairs/offense_location_keyword.rb +2 -0
  33. data/lib/rubocop/cop/internal_affairs/redundant_described_class_as_subject.rb +62 -0
  34. data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +65 -0
  35. data/lib/rubocop/cop/internal_affairs/redundant_location_argument.rb +1 -0
  36. data/lib/rubocop/cop/internal_affairs/redundant_message_argument.rb +3 -0
  37. data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +4 -0
  38. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +2 -0
  39. data/lib/rubocop/cop/layout/block_alignment.rb +1 -0
  40. data/lib/rubocop/cop/layout/class_structure.rb +8 -2
  41. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +37 -17
  42. data/lib/rubocop/cop/layout/extra_spacing.rb +2 -2
  43. data/lib/rubocop/cop/layout/first_argument_indentation.rb +22 -3
  44. data/lib/rubocop/cop/layout/indentation_width.rb +1 -0
  45. data/lib/rubocop/cop/layout/line_length.rb +2 -1
  46. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +26 -0
  47. data/lib/rubocop/cop/layout/space_before_block_braces.rb +1 -1
  48. data/lib/rubocop/cop/layout/space_before_brackets.rb +9 -4
  49. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +1 -1
  50. data/lib/rubocop/cop/lint/big_decimal_new.rb +1 -0
  51. data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -0
  52. data/lib/rubocop/cop/lint/constant_definition_in_block.rb +2 -0
  53. data/lib/rubocop/cop/lint/constant_resolution.rb +1 -0
  54. data/lib/rubocop/cop/lint/debugger.rb +60 -14
  55. data/lib/rubocop/cop/lint/deprecated_constants.rb +5 -0
  56. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +14 -4
  57. data/lib/rubocop/cop/lint/duplicate_branch.rb +1 -1
  58. data/lib/rubocop/cop/lint/duplicate_methods.rb +3 -0
  59. data/lib/rubocop/cop/lint/duplicate_require.rb +3 -2
  60. data/lib/rubocop/cop/lint/each_with_object_argument.rb +1 -0
  61. data/lib/rubocop/cop/lint/else_layout.rb +1 -1
  62. data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -0
  63. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +1 -0
  64. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +1 -0
  65. data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +1 -0
  66. data/lib/rubocop/cop/lint/inherit_exception.rb +1 -0
  67. data/lib/rubocop/cop/lint/multiple_comparison.rb +5 -4
  68. data/lib/rubocop/cop/lint/nested_method_definition.rb +3 -0
  69. data/lib/rubocop/cop/lint/next_without_accumulator.rb +1 -0
  70. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +7 -0
  71. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +3 -0
  72. data/lib/rubocop/cop/lint/number_conversion.rb +43 -6
  73. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +47 -0
  74. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +39 -0
  75. data/lib/rubocop/cop/lint/raise_exception.rb +2 -0
  76. data/lib/rubocop/cop/lint/rand_one.rb +1 -0
  77. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +5 -3
  78. data/lib/rubocop/cop/lint/redundant_require_statement.rb +1 -0
  79. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +1 -0
  80. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +7 -3
  81. data/lib/rubocop/cop/lint/redundant_string_coercion.rb +1 -0
  82. data/lib/rubocop/cop/lint/redundant_with_index.rb +1 -0
  83. data/lib/rubocop/cop/lint/redundant_with_object.rb +1 -0
  84. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +1 -0
  85. data/lib/rubocop/cop/lint/safe_navigation_with_empty.rb +1 -0
  86. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +1 -0
  87. data/lib/rubocop/cop/lint/shadowed_argument.rb +1 -0
  88. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +1 -0
  89. data/lib/rubocop/cop/lint/struct_new_override.rb +1 -0
  90. data/lib/rubocop/cop/lint/symbol_conversion.rb +103 -0
  91. data/lib/rubocop/cop/lint/to_enum_arguments.rb +3 -0
  92. data/lib/rubocop/cop/lint/triple_quotes.rb +71 -0
  93. data/lib/rubocop/cop/lint/unified_integer.rb +1 -0
  94. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +5 -0
  95. data/lib/rubocop/cop/lint/unreachable_code.rb +1 -0
  96. data/lib/rubocop/cop/lint/unreachable_loop.rb +1 -0
  97. data/lib/rubocop/cop/lint/unused_method_argument.rb +1 -0
  98. data/lib/rubocop/cop/lint/uri_escape_unescape.rb +1 -0
  99. data/lib/rubocop/cop/lint/useless_access_modifier.rb +4 -0
  100. data/lib/rubocop/cop/lint/useless_setter_call.rb +1 -0
  101. data/lib/rubocop/cop/lint/useless_times.rb +3 -0
  102. data/lib/rubocop/cop/message_annotator.rb +4 -1
  103. data/lib/rubocop/cop/metrics/block_nesting.rb +2 -2
  104. data/lib/rubocop/cop/metrics/module_length.rb +1 -0
  105. data/lib/rubocop/cop/metrics/parameter_lists.rb +6 -2
  106. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +6 -4
  107. data/lib/rubocop/cop/metrics/utils/repeated_attribute_discount.rb +2 -0
  108. data/lib/rubocop/cop/mixin/check_line_breakable.rb +5 -0
  109. data/lib/rubocop/cop/mixin/code_length.rb +3 -1
  110. data/lib/rubocop/cop/mixin/comments_help.rb +0 -1
  111. data/lib/rubocop/cop/mixin/configurable_max.rb +1 -0
  112. data/lib/rubocop/cop/mixin/def_node.rb +1 -0
  113. data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +3 -0
  114. data/lib/rubocop/cop/mixin/empty_parameter.rb +1 -0
  115. data/lib/rubocop/cop/mixin/enforce_superclass.rb +2 -0
  116. data/lib/rubocop/cop/mixin/hash_transform_method.rb +1 -0
  117. data/lib/rubocop/cop/mixin/method_complexity.rb +4 -1
  118. data/lib/rubocop/cop/mixin/negative_conditional.rb +3 -0
  119. data/lib/rubocop/cop/mixin/preferred_delimiters.rb +3 -3
  120. data/lib/rubocop/cop/mixin/rational_literal.rb +1 -0
  121. data/lib/rubocop/cop/mixin/safe_assignment.rb +5 -0
  122. data/lib/rubocop/cop/mixin/uncommunicative_name.rb +5 -1
  123. data/lib/rubocop/cop/mixin/visibility_help.rb +1 -0
  124. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +1 -0
  125. data/lib/rubocop/cop/naming/constant_name.rb +2 -0
  126. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +2 -0
  127. data/lib/rubocop/cop/naming/method_name.rb +3 -0
  128. data/lib/rubocop/cop/naming/predicate_name.rb +1 -0
  129. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +38 -5
  130. data/lib/rubocop/cop/naming/variable_number.rb +1 -1
  131. data/lib/rubocop/cop/registry.rb +1 -1
  132. data/lib/rubocop/cop/security/eval.rb +1 -0
  133. data/lib/rubocop/cop/security/json_load.rb +1 -0
  134. data/lib/rubocop/cop/security/marshal_load.rb +1 -0
  135. data/lib/rubocop/cop/security/open.rb +1 -0
  136. data/lib/rubocop/cop/security/yaml_load.rb +1 -0
  137. data/lib/rubocop/cop/severity.rb +3 -3
  138. data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -0
  139. data/lib/rubocop/cop/style/alias.rb +1 -0
  140. data/lib/rubocop/cop/style/arguments_forwarding.rb +3 -0
  141. data/lib/rubocop/cop/style/array_coercion.rb +2 -0
  142. data/lib/rubocop/cop/style/array_join.rb +1 -0
  143. data/lib/rubocop/cop/style/ascii_comments.rb +1 -1
  144. data/lib/rubocop/cop/style/attr.rb +1 -0
  145. data/lib/rubocop/cop/style/case_equality.rb +2 -1
  146. data/lib/rubocop/cop/style/class_equality_comparison.rb +1 -0
  147. data/lib/rubocop/cop/style/collection_compact.rb +2 -0
  148. data/lib/rubocop/cop/style/colon_method_call.rb +1 -0
  149. data/lib/rubocop/cop/style/command_literal.rb +1 -1
  150. data/lib/rubocop/cop/style/conditional_assignment.rb +2 -0
  151. data/lib/rubocop/cop/style/constant_visibility.rb +28 -0
  152. data/lib/rubocop/cop/style/date_time.rb +3 -0
  153. data/lib/rubocop/cop/style/dir.rb +1 -0
  154. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +49 -9
  155. data/lib/rubocop/cop/style/documentation.rb +5 -0
  156. data/lib/rubocop/cop/style/documentation_method.rb +1 -0
  157. data/lib/rubocop/cop/style/double_negation.rb +3 -2
  158. data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -0
  159. data/lib/rubocop/cop/style/each_with_object.rb +1 -0
  160. data/lib/rubocop/cop/style/empty_literal.rb +9 -0
  161. data/lib/rubocop/cop/style/endless_method.rb +1 -0
  162. data/lib/rubocop/cop/style/eval_with_location.rb +140 -49
  163. data/lib/rubocop/cop/style/even_odd.rb +1 -0
  164. data/lib/rubocop/cop/style/expand_path_arguments.rb +3 -0
  165. data/lib/rubocop/cop/style/explicit_block_argument.rb +12 -1
  166. data/lib/rubocop/cop/style/exponential_notation.rb +6 -7
  167. data/lib/rubocop/cop/style/float_division.rb +7 -0
  168. data/lib/rubocop/cop/style/format_string.rb +2 -0
  169. data/lib/rubocop/cop/style/format_string_token.rb +19 -2
  170. data/lib/rubocop/cop/style/global_std_stream.rb +1 -0
  171. data/lib/rubocop/cop/style/hash_conversion.rb +105 -0
  172. data/lib/rubocop/cop/style/hash_each_methods.rb +1 -0
  173. data/lib/rubocop/cop/style/hash_except.rb +1 -0
  174. data/lib/rubocop/cop/style/hash_like_case.rb +1 -0
  175. data/lib/rubocop/cop/style/hash_transform_keys.rb +4 -0
  176. data/lib/rubocop/cop/style/hash_transform_values.rb +4 -0
  177. data/lib/rubocop/cop/style/if_inside_else.rb +14 -7
  178. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +122 -0
  179. data/lib/rubocop/cop/style/implicit_runtime_error.rb +1 -0
  180. data/lib/rubocop/cop/style/inverse_methods.rb +2 -0
  181. data/lib/rubocop/cop/style/min_max.rb +1 -0
  182. data/lib/rubocop/cop/style/mixin_usage.rb +2 -0
  183. data/lib/rubocop/cop/style/module_function.rb +5 -0
  184. data/lib/rubocop/cop/style/multiple_comparison.rb +21 -2
  185. data/lib/rubocop/cop/style/mutable_constant.rb +3 -0
  186. data/lib/rubocop/cop/style/negated_if_else_condition.rb +1 -0
  187. data/lib/rubocop/cop/style/nil_comparison.rb +6 -0
  188. data/lib/rubocop/cop/style/nil_lambda.rb +1 -0
  189. data/lib/rubocop/cop/style/non_nil_check.rb +30 -13
  190. data/lib/rubocop/cop/style/numeric_literals.rb +6 -9
  191. data/lib/rubocop/cop/style/numeric_predicate.rb +4 -1
  192. data/lib/rubocop/cop/style/option_hash.rb +1 -0
  193. data/lib/rubocop/cop/style/or_assignment.rb +2 -0
  194. data/lib/rubocop/cop/style/parallel_assignment.rb +6 -0
  195. data/lib/rubocop/cop/style/parentheses_around_condition.rb +1 -0
  196. data/lib/rubocop/cop/style/proc.rb +1 -0
  197. data/lib/rubocop/cop/style/random_with_offset.rb +5 -0
  198. data/lib/rubocop/cop/style/redundant_assignment.rb +1 -0
  199. data/lib/rubocop/cop/style/redundant_begin.rb +7 -1
  200. data/lib/rubocop/cop/style/redundant_conditional.rb +2 -0
  201. data/lib/rubocop/cop/style/redundant_exception.rb +2 -0
  202. data/lib/rubocop/cop/style/redundant_fetch_block.rb +2 -0
  203. data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +1 -0
  204. data/lib/rubocop/cop/style/redundant_freeze.rb +1 -0
  205. data/lib/rubocop/cop/style/redundant_parentheses.rb +13 -0
  206. data/lib/rubocop/cop/style/redundant_self_assignment.rb +2 -0
  207. data/lib/rubocop/cop/style/redundant_sort.rb +1 -0
  208. data/lib/rubocop/cop/style/redundant_sort_by.rb +1 -0
  209. data/lib/rubocop/cop/style/regexp_literal.rb +1 -1
  210. data/lib/rubocop/cop/style/rescue_standard_error.rb +2 -0
  211. data/lib/rubocop/cop/style/return_nil.rb +6 -0
  212. data/lib/rubocop/cop/style/safe_navigation.rb +2 -0
  213. data/lib/rubocop/cop/style/sample.rb +1 -0
  214. data/lib/rubocop/cop/style/signal_exception.rb +3 -0
  215. data/lib/rubocop/cop/style/single_argument_dig.rb +1 -0
  216. data/lib/rubocop/cop/style/single_line_methods.rb +5 -2
  217. data/lib/rubocop/cop/style/slicing_with_range.rb +1 -0
  218. data/lib/rubocop/cop/style/sole_nested_conditional.rb +28 -4
  219. data/lib/rubocop/cop/style/special_global_vars.rb +3 -3
  220. data/lib/rubocop/cop/style/stderr_puts.rb +1 -0
  221. data/lib/rubocop/cop/style/string_concatenation.rb +2 -1
  222. data/lib/rubocop/cop/style/string_hash_keys.rb +2 -0
  223. data/lib/rubocop/cop/style/strip.rb +1 -0
  224. data/lib/rubocop/cop/style/struct_inheritance.rb +1 -0
  225. data/lib/rubocop/cop/style/symbol_proc.rb +25 -1
  226. data/lib/rubocop/cop/style/ternary_parentheses.rb +2 -1
  227. data/lib/rubocop/cop/style/trailing_body_on_method_definition.rb +1 -0
  228. data/lib/rubocop/cop/style/trailing_method_end_statement.rb +1 -1
  229. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -0
  230. data/lib/rubocop/cop/style/unless_logical_operators.rb +99 -0
  231. data/lib/rubocop/cop/style/unpack_first.rb +1 -0
  232. data/lib/rubocop/cop/style/while_until_modifier.rb +2 -4
  233. data/lib/rubocop/cop/style/yoda_condition.rb +1 -0
  234. data/lib/rubocop/cop/style/zero_length_predicate.rb +5 -0
  235. data/lib/rubocop/formatter/git_hub_actions_formatter.rb +1 -0
  236. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  237. data/lib/rubocop/formatter/simple_text_formatter.rb +2 -1
  238. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
  239. data/lib/rubocop/magic_comment.rb +30 -1
  240. data/lib/rubocop/name_similarity.rb +1 -1
  241. data/lib/rubocop/options.rb +1 -1
  242. data/lib/rubocop/rspec/expect_offense.rb +5 -2
  243. data/lib/rubocop/runner.rb +1 -0
  244. data/lib/rubocop/target_ruby.rb +21 -13
  245. data/lib/rubocop/version.rb +2 -2
  246. metadata +21 -7
@@ -21,6 +21,7 @@ module RuboCop
21
21
  MSG = 'Replace with `Integer#%<method>s?`.'
22
22
  RESTRICT_ON_SEND = %i[== !=].freeze
23
23
 
24
+ # @!method even_odd_candidate?(node)
24
25
  def_node_matcher :even_odd_candidate?, <<~PATTERN
25
26
  (send
26
27
  {(send $_ :% (int 2))
@@ -54,6 +54,7 @@ module RuboCop
54
54
 
55
55
  RESTRICT_ON_SEND = %i[expand_path].freeze
56
56
 
57
+ # @!method file_expand_path(node)
57
58
  def_node_matcher :file_expand_path, <<~PATTERN
58
59
  (send
59
60
  (const {nil? cbase} :File) :expand_path
@@ -61,6 +62,7 @@ module RuboCop
61
62
  $_)
62
63
  PATTERN
63
64
 
65
+ # @!method pathname_parent_expand_path(node)
64
66
  def_node_matcher :pathname_parent_expand_path, <<~PATTERN
65
67
  (send
66
68
  (send
@@ -68,6 +70,7 @@ module RuboCop
68
70
  $_) :parent) :expand_path)
69
71
  PATTERN
70
72
 
73
+ # @!method pathname_new_parent_expand_path(node)
71
74
  def_node_matcher :pathname_new_parent_expand_path, <<~PATTERN
72
75
  (send
73
76
  (send
@@ -6,6 +6,9 @@ module RuboCop
6
6
  # This cop enforces the use of explicit block argument to avoid writing
7
7
  # block literal that just passes its arguments to another block.
8
8
  #
9
+ # NOTE: This cop only registers an offense if the block args match the
10
+ # yield args exactly.
11
+ #
9
12
  # @example
10
13
  # # bad
11
14
  # def with_tmp_dir
@@ -42,6 +45,7 @@ module RuboCop
42
45
  MSG = 'Consider using explicit block argument in the '\
43
46
  "surrounding method's signature over `yield`."
44
47
 
48
+ # @!method yielding_block?(node)
45
49
  def_node_matcher :yielding_block?, <<~PATTERN
46
50
  (block $_ (args $...) (yield $...))
47
51
  PATTERN
@@ -75,7 +79,14 @@ module RuboCop
75
79
  private
76
80
 
77
81
  def yielding_arguments?(block_args, yield_args)
82
+ yield_args = yield_args.dup.fill(
83
+ nil,
84
+ yield_args.length, block_args.length - yield_args.length
85
+ )
86
+
78
87
  yield_args.zip(block_args).all? do |yield_arg, block_arg|
88
+ next false unless yield_arg && block_arg
89
+
79
90
  block_arg && yield_arg.children.first == block_arg.children.first
80
91
  end
81
92
  end
@@ -87,7 +98,7 @@ module RuboCop
87
98
  replacement = ' &block'
88
99
  replacement = ",#{replacement}" unless arg_range.source.end_with?(',')
89
100
  corrector.insert_after(arg_range, replacement) unless last_arg.blockarg_type?
90
- elsif node.call_type?
101
+ elsif node.call_type? || node.zsuper_type?
91
102
  corrector.insert_after(node, '(&block)')
92
103
  else
93
104
  corrector.insert_after(node.loc.name, '(&block)')
@@ -5,13 +5,12 @@ module RuboCop
5
5
  module Style
6
6
  # This cop enforces consistency when using exponential notation
7
7
  # for numbers in the code (eg 1.2e4). Different styles are supported:
8
- # * `scientific` which enforces a mantissa between 1 (inclusive)
9
- # and 10 (exclusive).
10
- # * `engineering` which enforces the exponent to be a multiple of 3
11
- # and the mantissa to be between 0.1 (inclusive)
12
- # and 10 (exclusive).
13
- # * `integral` which enforces the mantissa to always be a whole number
14
- # without trailing zeroes.
8
+ #
9
+ # * `scientific` which enforces a mantissa between 1 (inclusive) and 10 (exclusive).
10
+ # * `engineering` which enforces the exponent to be a multiple of 3 and the mantissa
11
+ # to be between 0.1 (inclusive) and 10 (exclusive).
12
+ # * `integral` which enforces the mantissa to always be a whole number without
13
+ # trailing zeroes.
15
14
  #
16
15
  # @example EnforcedStyle: scientific (default)
17
16
  # # Enforces a mantissa between 1 (inclusive) and 10 (exclusive).
@@ -7,6 +7,9 @@ module RuboCop
7
7
  # It is recommended to either always use `fdiv` or coerce one side only.
8
8
  # This cop also provides other options for code consistency.
9
9
  #
10
+ # This cop is marked as unsafe, because if operand variable is a string object
11
+ # then `.to_f` will be removed and an error will occur.
12
+ #
10
13
  # @example EnforcedStyle: single_coerce (default)
11
14
  # # bad
12
15
  # a.to_f / b.to_f
@@ -52,15 +55,19 @@ module RuboCop
52
55
 
53
56
  RESTRICT_ON_SEND = %i[/].freeze
54
57
 
58
+ # @!method right_coerce?(node)
55
59
  def_node_matcher :right_coerce?, <<~PATTERN
56
60
  (send _ :/ (send _ :to_f))
57
61
  PATTERN
62
+ # @!method left_coerce?(node)
58
63
  def_node_matcher :left_coerce?, <<~PATTERN
59
64
  (send (send _ :to_f) :/ _)
60
65
  PATTERN
66
+ # @!method both_coerce?(node)
61
67
  def_node_matcher :both_coerce?, <<~PATTERN
62
68
  (send (send _ :to_f) :/ (send _ :to_f))
63
69
  PATTERN
70
+ # @!method any_coerce?(node)
64
71
  def_node_matcher :any_coerce?, <<~PATTERN
65
72
  {(send _ :/ (send _ :to_f)) (send (send _ :to_f) :/ _)}
66
73
  PATTERN
@@ -42,6 +42,7 @@ module RuboCop
42
42
  MSG = 'Favor `%<prefer>s` over `%<current>s`.'
43
43
  RESTRICT_ON_SEND = %i[format sprintf %].freeze
44
44
 
45
+ # @!method formatter(node)
45
46
  def_node_matcher :formatter, <<~PATTERN
46
47
  {
47
48
  (send nil? ${:sprintf :format} _ _ ...)
@@ -50,6 +51,7 @@ module RuboCop
50
51
  }
51
52
  PATTERN
52
53
 
54
+ # @!method variable_argument?(node)
53
55
  def_node_matcher :variable_argument?, <<~PATTERN
54
56
  (send {str dstr} :% {send_type? lvar_type?})
55
57
  PATTERN
@@ -11,6 +11,8 @@ module RuboCop
11
11
  # The reason is that _unannotated_ format is very similar
12
12
  # to encoded URLs or Date/Time formatting strings.
13
13
  #
14
+ # This cop can be customized ignored methods with `IgnoredMethods`.
15
+ #
14
16
  # @example EnforcedStyle: annotated (default)
15
17
  #
16
18
  # # bad
@@ -58,12 +60,18 @@ module RuboCop
58
60
  #
59
61
  # # good
60
62
  # format('%06d', 10)
63
+ #
64
+ # @example IgnoredMethods: [redirect]
65
+ #
66
+ # # good
67
+ # redirect('foo/%{bar_id}')
68
+ #
61
69
  class FormatStringToken < Base
62
70
  include ConfigurableEnforcedStyle
71
+ include IgnoredMethods
63
72
 
64
73
  def on_str(node)
65
- return unless node.value.include?('%')
66
- return if node.each_ancestor(:xstr, :regexp).any?
74
+ return if format_string_token?(node) || use_ignored_method?(node)
67
75
 
68
76
  detections = collect_detections(node)
69
77
  return if detections.empty?
@@ -81,6 +89,7 @@ module RuboCop
81
89
 
82
90
  private
83
91
 
92
+ # @!method format_string_in_typical_context?(node)
84
93
  def_node_matcher :format_string_in_typical_context?, <<~PATTERN
85
94
  {
86
95
  ^(send _ {:format :sprintf :printf} %0 ...)
@@ -88,6 +97,14 @@ module RuboCop
88
97
  }
89
98
  PATTERN
90
99
 
100
+ def format_string_token?(node)
101
+ !node.value.include?('%') || node.each_ancestor(:xstr, :regexp).any?
102
+ end
103
+
104
+ def use_ignored_method?(node)
105
+ (parent = node.parent) && parent.send_type? && ignored_method?(parent.method_name)
106
+ end
107
+
91
108
  def unannotated_format?(node, detected_style)
92
109
  detected_style == :unannotated && !format_string_in_typical_context?(node)
93
110
  end
@@ -34,6 +34,7 @@ module RuboCop
34
34
 
35
35
  STD_STREAMS = %i[STDIN STDOUT STDERR].to_set.freeze
36
36
 
37
+ # @!method const_to_gvar_assignment?(node, name)
37
38
  def_node_matcher :const_to_gvar_assignment?, <<~PATTERN
38
39
  (gvasgn %1 (const nil? _))
39
40
  PATTERN
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop checks the usage of pre-2.1 `Hash[args]` method of converting enumerables and
7
+ # sequences of values to hashes.
8
+ #
9
+ # Correction code from splat argument (`Hash[*ary]`) is not simply determined. For example,
10
+ # `Hash[*ary]` can be replaced with `ary.each_slice(2).to_h` but it will be complicated.
11
+ # So, `AllowSplatArgument` option is true by default to allow splat argument for simple code.
12
+ #
13
+ # @example
14
+ # # bad
15
+ # Hash[ary]
16
+ #
17
+ # # good
18
+ # ary.to_h
19
+ #
20
+ # # bad
21
+ # Hash[key1, value1, key2, value2]
22
+ #
23
+ # # good
24
+ # {key1 => value1, key2 => value2}
25
+ #
26
+ # @example AllowSplatArgument: true (default)
27
+ # # good
28
+ # Hash[*ary]
29
+ #
30
+ # @example AllowSplatArgument: false
31
+ # # bad
32
+ # Hash[*ary]
33
+ #
34
+ class HashConversion < Base
35
+ extend AutoCorrector
36
+
37
+ MSG_TO_H = 'Prefer ary.to_h to Hash[ary].'
38
+ MSG_LITERAL_MULTI_ARG = 'Prefer literal hash to Hash[arg1, arg2, ...].'
39
+ MSG_LITERAL_HASH_ARG = 'Prefer literal hash to Hash[key: value, ...].'
40
+ MSG_SPLAT = 'Prefer array_of_pairs.to_h to Hash[*array].'
41
+ RESTRICT_ON_SEND = %i[[]].freeze
42
+
43
+ # @!method hash_from_array?(node)
44
+ def_node_matcher :hash_from_array?, '(send (const {nil? cbase} :Hash) :[] ...)'
45
+
46
+ def on_send(node)
47
+ return unless hash_from_array?(node)
48
+
49
+ # There are several cases:
50
+ # If there is one argument:
51
+ # Hash[ary] => ary.to_h
52
+ # Hash[*ary] => don't suggest corrections
53
+ # If there is 0 or 2+ arguments:
54
+ # Hash[a1, a2, a3, a4] => {a1 => a2, a3 => a4}
55
+ # ...but don't suggest correction if there is odd number of them (it is a bug)
56
+ node.arguments.count == 1 ? single_argument(node) : multi_argument(node)
57
+ end
58
+
59
+ private
60
+
61
+ def single_argument(node)
62
+ first_argument = node.first_argument
63
+ if first_argument.hash_type?
64
+ add_offense(node, message: MSG_LITERAL_HASH_ARG) do |corrector|
65
+ corrector.replace(node, "{#{first_argument.source}}")
66
+ end
67
+ elsif first_argument.splat_type?
68
+ add_offense(node, message: MSG_SPLAT) unless allowed_splat_argument?
69
+ else
70
+ add_offense(node, message: MSG_TO_H) do |corrector|
71
+ replacement = first_argument.source
72
+ replacement = "(#{replacement})" if requires_parens?(first_argument)
73
+ corrector.replace(node, "#{replacement}.to_h")
74
+ end
75
+ end
76
+ end
77
+
78
+ def requires_parens?(node)
79
+ node.call_type? && node.arguments.any? && !node.parenthesized?
80
+ end
81
+
82
+ def multi_argument(node)
83
+ if node.arguments.count.odd?
84
+ add_offense(node, message: MSG_LITERAL_MULTI_ARG)
85
+ else
86
+ add_offense(node, message: MSG_LITERAL_MULTI_ARG) do |corrector|
87
+ corrector.replace(node, args_to_hash(node.arguments))
88
+ end
89
+ end
90
+ end
91
+
92
+ def args_to_hash(args)
93
+ content = args.each_slice(2)
94
+ .map { |arg1, arg2| "#{arg1.source} => #{arg2.source}" }
95
+ .join(', ')
96
+ "{#{content}}"
97
+ end
98
+
99
+ def allowed_splat_argument?
100
+ cop_config.fetch('AllowSplatArgument', true)
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -23,6 +23,7 @@ module RuboCop
23
23
 
24
24
  MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
25
25
 
26
+ # @!method kv_each(node)
26
27
  def_node_matcher :kv_each, <<~PATTERN
27
28
  (block $(send (send _ ${:keys :values}) :each) ...)
28
29
  PATTERN
@@ -33,6 +33,7 @@ module RuboCop
33
33
  MSG = 'Use `%<prefer>s` instead.'
34
34
  RESTRICT_ON_SEND = %i[reject select filter].freeze
35
35
 
36
+ # @!method bad_method?(node)
36
37
  def_node_matcher :bad_method?, <<~PATTERN
37
38
  (block
38
39
  (send _ _)
@@ -39,6 +39,7 @@ module RuboCop
39
39
  class HashLikeCase < Base
40
40
  MSG = 'Consider replacing `case-when` with a hash lookup.'
41
41
 
42
+ # @!method hash_like_case?(node)
42
43
  def_node_matcher :hash_like_case?, <<~PATTERN
43
44
  (case
44
45
  _
@@ -32,6 +32,7 @@ module RuboCop
32
32
 
33
33
  minimum_target_ruby_version 2.5
34
34
 
35
+ # @!method on_bad_each_with_object(node)
35
36
  def_node_matcher :on_bad_each_with_object, <<~PATTERN
36
37
  (block
37
38
  ({send csend} !#array_receiver? :each_with_object (hash))
@@ -43,6 +44,7 @@ module RuboCop
43
44
  ({send csend} (lvar _memo) :[]= $!`_memo $(lvar _val)))
44
45
  PATTERN
45
46
 
47
+ # @!method on_bad_hash_brackets_map(node)
46
48
  def_node_matcher :on_bad_hash_brackets_map, <<~PATTERN
47
49
  (send
48
50
  (const _ :Hash)
@@ -55,6 +57,7 @@ module RuboCop
55
57
  (array $_ $(lvar _val))))
56
58
  PATTERN
57
59
 
60
+ # @!method on_bad_map_to_h(node)
58
61
  def_node_matcher :on_bad_map_to_h, <<~PATTERN
59
62
  ({send csend}
60
63
  (block
@@ -66,6 +69,7 @@ module RuboCop
66
69
  :to_h)
67
70
  PATTERN
68
71
 
72
+ # @!method on_bad_to_h(node)
69
73
  def_node_matcher :on_bad_to_h, <<~PATTERN
70
74
  (block
71
75
  ({send csend} !#array_receiver? :to_h)
@@ -29,6 +29,7 @@ module RuboCop
29
29
  include HashTransformMethod
30
30
  extend AutoCorrector
31
31
 
32
+ # @!method on_bad_each_with_object(node)
32
33
  def_node_matcher :on_bad_each_with_object, <<~PATTERN
33
34
  (block
34
35
  ({send csend} !#array_receiver? :each_with_object (hash))
@@ -40,6 +41,7 @@ module RuboCop
40
41
  ({send csend} (lvar _memo) :[]= $(lvar _key) $!`_memo))
41
42
  PATTERN
42
43
 
44
+ # @!method on_bad_hash_brackets_map(node)
43
45
  def_node_matcher :on_bad_hash_brackets_map, <<~PATTERN
44
46
  (send
45
47
  (const _ :Hash)
@@ -52,6 +54,7 @@ module RuboCop
52
54
  (array $(lvar _key) $_)))
53
55
  PATTERN
54
56
 
57
+ # @!method on_bad_map_to_h(node)
55
58
  def_node_matcher :on_bad_map_to_h, <<~PATTERN
56
59
  ({send csend}
57
60
  (block
@@ -63,6 +66,7 @@ module RuboCop
63
66
  :to_h)
64
67
  PATTERN
65
68
 
69
+ # @!method on_bad_to_h(node)
66
70
  def_node_matcher :on_bad_to_h, <<~PATTERN
67
71
  (block
68
72
  ({send csend} !#array_receiver? :to_h)
@@ -86,9 +86,10 @@ module RuboCop
86
86
  correct_to_elsif_from_if_inside_else_form(corrector, node, node.condition)
87
87
  end
88
88
  corrector.remove(range_by_whole_lines(find_end_range(node), include_final_newline: true))
89
- corrector.remove(
90
- range_by_whole_lines(node.if_branch.source_range, include_final_newline: true)
91
- )
89
+ return unless (if_branch = node.if_branch)
90
+
91
+ range = range_by_whole_lines(if_branch.source_range, include_final_newline: true)
92
+ corrector.remove(range)
92
93
  end
93
94
 
94
95
  def correct_to_elsif_from_modifier_form(corrector, node)
@@ -101,10 +102,12 @@ module RuboCop
101
102
 
102
103
  def correct_to_elsif_from_if_inside_else_form(corrector, node, condition)
103
104
  corrector.replace(node.parent.loc.else, "elsif #{condition.source}")
104
- if_condition_range = range_between(
105
- node.loc.keyword.begin_pos, condition.source_range.end_pos
106
- )
107
- corrector.replace(if_condition_range, node.if_branch.source)
105
+ if_condition_range = if_condition_range(node, condition)
106
+ if (if_branch = node.if_branch)
107
+ corrector.replace(if_condition_range, if_branch.source)
108
+ else
109
+ corrector.remove(range_by_whole_lines(if_condition_range, include_final_newline: true))
110
+ end
108
111
  corrector.remove(condition)
109
112
  end
110
113
 
@@ -115,6 +118,10 @@ module RuboCop
115
118
  find_end_range(node.parent)
116
119
  end
117
120
 
121
+ def if_condition_range(node, condition)
122
+ range_between(node.loc.keyword.begin_pos, condition.source_range.end_pos)
123
+ end
124
+
118
125
  def allow_if_modifier_in_else_branch?(else_branch)
119
126
  allow_if_modifier? && else_branch&.modifier_form?
120
127
  end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop checks for redundant `if` with boolean literal branches.
7
+ # It checks only conditions to return boolean value (`true` or `false`) for safe detection.
8
+ # The conditions to be checked are comparison methods, predicate methods, and double negative.
9
+ # However, auto-correction is unsafe because there is no guarantee that all predicate methods
10
+ # will return boolean value. Those methods can be allowed with `AllowedMethods` config.
11
+ #
12
+ # @example
13
+ # # bad
14
+ # if foo == bar
15
+ # true
16
+ # else
17
+ # false
18
+ # end
19
+ #
20
+ # # bad
21
+ # foo == bar ? true : false
22
+ #
23
+ # # good
24
+ # foo == bar
25
+ #
26
+ # @example AllowedMethods: ['nonzero?']
27
+ # # good
28
+ # num.nonzero? ? true : false
29
+ #
30
+ class IfWithBooleanLiteralBranches < Base
31
+ include AllowedMethods
32
+ extend AutoCorrector
33
+
34
+ MSG = 'Remove redundant %<keyword>s with boolean literal branches.'
35
+ MSG_FOR_ELSIF = 'Use `else` instead of redundant `elsif` with boolean literal branches.'
36
+
37
+ # @!method if_with_boolean_literal_branches?(node)
38
+ def_node_matcher :if_with_boolean_literal_branches?, <<~PATTERN
39
+ (if #return_boolean_value? {(true) (false) | (false) (true)})
40
+ PATTERN
41
+ # @!method double_negative?(node)
42
+ def_node_matcher :double_negative?, '(send (send _ :!) :!)'
43
+
44
+ def on_if(node)
45
+ return unless if_with_boolean_literal_branches?(node)
46
+
47
+ condition = node.condition
48
+ range, keyword = offense_range_with_keyword(node, condition)
49
+
50
+ add_offense(range, message: message(node, keyword)) do |corrector|
51
+ replacement = replacement_condition(node, condition)
52
+
53
+ if node.elsif?
54
+ corrector.insert_before(node, "else\n")
55
+ corrector.replace(node, "#{indent(node.if_branch)}#{replacement}")
56
+ else
57
+ corrector.replace(node, replacement)
58
+ end
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def offense_range_with_keyword(node, condition)
65
+ if node.ternary?
66
+ range = condition.source_range.end.join(node.source_range.end)
67
+
68
+ [range, 'ternary operator']
69
+ else
70
+ keyword = node.loc.keyword
71
+
72
+ [keyword, "`#{keyword.source}`"]
73
+ end
74
+ end
75
+
76
+ def message(node, keyword)
77
+ message_template = node.elsif? ? MSG_FOR_ELSIF : MSG
78
+
79
+ format(message_template, keyword: keyword)
80
+ end
81
+
82
+ def return_boolean_value?(condition)
83
+ if condition.begin_type?
84
+ return_boolean_value?(condition.children.first)
85
+ elsif condition.or_type?
86
+ return_boolean_value?(condition.lhs) && return_boolean_value?(condition.rhs)
87
+ elsif condition.and_type?
88
+ return_boolean_value?(condition.rhs)
89
+ else
90
+ assume_boolean_value?(condition)
91
+ end
92
+ end
93
+
94
+ def assume_boolean_value?(condition)
95
+ return false unless condition.send_type?
96
+ return false if allowed_method?(condition.method_name)
97
+
98
+ condition.comparison_method? || condition.predicate_method? || double_negative?(condition)
99
+ end
100
+
101
+ def replacement_condition(node, condition)
102
+ bang = '!' if opposite_condition?(node)
103
+
104
+ if bang && require_parentheses?(condition)
105
+ "#{bang}(#{condition.source})"
106
+ else
107
+ "#{bang}#{condition.source}"
108
+ end
109
+ end
110
+
111
+ def opposite_condition?(node)
112
+ !node.unless? && node.if_branch.false_type? || node.unless? && node.if_branch.true_type?
113
+ end
114
+
115
+ def require_parentheses?(condition)
116
+ condition.and_type? || condition.or_type? ||
117
+ condition.send_type? && condition.comparison_method?
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end