rubocop 1.75.8 → 1.80.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 (131) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +20 -16
  3. data/config/default.yml +107 -26
  4. data/config/obsoletion.yml +6 -3
  5. data/exe/rubocop +1 -8
  6. data/lib/rubocop/cli.rb +17 -1
  7. data/lib/rubocop/config_loader.rb +1 -38
  8. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -1
  9. data/lib/rubocop/cop/correctors/alignment_corrector.rb +6 -3
  10. data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +7 -2
  11. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +5 -2
  12. data/lib/rubocop/cop/gemspec/attribute_assignment.rb +91 -0
  13. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +0 -22
  14. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
  15. data/lib/rubocop/cop/gemspec/require_mfa.rb +15 -1
  16. data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
  17. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +4 -4
  18. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +1 -0
  19. data/lib/rubocop/cop/internal_affairs/node_type_group.rb +3 -2
  20. data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +1 -1
  21. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +1 -1
  22. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +101 -0
  23. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -1
  24. data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +8 -29
  25. data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +1 -1
  26. data/lib/rubocop/cop/layout/line_length.rb +26 -5
  27. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -0
  28. data/lib/rubocop/cop/layout/space_around_keyword.rb +6 -1
  29. data/lib/rubocop/cop/layout/space_around_operators.rb +8 -0
  30. data/lib/rubocop/cop/layout/space_before_brackets.rb +2 -9
  31. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +7 -2
  32. data/lib/rubocop/cop/lint/ambiguous_range.rb +5 -0
  33. data/lib/rubocop/cop/lint/duplicate_methods.rb +25 -4
  34. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +5 -42
  35. data/lib/rubocop/cop/lint/empty_interpolation.rb +3 -1
  36. data/lib/rubocop/cop/lint/float_comparison.rb +4 -4
  37. data/lib/rubocop/cop/lint/identity_comparison.rb +19 -15
  38. data/lib/rubocop/cop/lint/literal_as_condition.rb +34 -28
  39. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +1 -2
  40. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +1 -0
  41. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
  42. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +101 -2
  43. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +4 -4
  44. data/lib/rubocop/cop/lint/require_range_parentheses.rb +1 -1
  45. data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
  46. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +4 -4
  47. data/lib/rubocop/cop/lint/self_assignment.rb +30 -4
  48. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +5 -0
  49. data/lib/rubocop/cop/lint/useless_access_modifier.rb +29 -4
  50. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +90 -0
  51. data/lib/rubocop/cop/lint/useless_numeric_operation.rb +1 -0
  52. data/lib/rubocop/cop/lint/useless_or.rb +98 -0
  53. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +3 -3
  54. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +121 -0
  55. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  56. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -7
  57. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -1
  58. data/lib/rubocop/cop/mixin/gemspec_help.rb +22 -0
  59. data/lib/rubocop/cop/mixin/line_length_help.rb +24 -8
  60. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
  61. data/lib/rubocop/cop/naming/file_name.rb +2 -2
  62. data/lib/rubocop/cop/naming/method_name.rb +127 -13
  63. data/lib/rubocop/cop/naming/predicate_method.rb +306 -0
  64. data/lib/rubocop/cop/naming/{predicate_name.rb → predicate_prefix.rb} +4 -4
  65. data/lib/rubocop/cop/security/eval.rb +2 -1
  66. data/lib/rubocop/cop/security/open.rb +1 -0
  67. data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -1
  68. data/lib/rubocop/cop/style/accessor_grouping.rb +13 -1
  69. data/lib/rubocop/cop/style/arguments_forwarding.rb +11 -17
  70. data/lib/rubocop/cop/style/array_intersect.rb +98 -34
  71. data/lib/rubocop/cop/style/bitwise_predicate.rb +8 -1
  72. data/lib/rubocop/cop/style/block_delimiters.rb +1 -1
  73. data/lib/rubocop/cop/style/case_like_if.rb +1 -1
  74. data/lib/rubocop/cop/style/collection_querying.rb +167 -0
  75. data/lib/rubocop/cop/style/conditional_assignment.rb +4 -2
  76. data/lib/rubocop/cop/style/dig_chain.rb +1 -1
  77. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +100 -0
  78. data/lib/rubocop/cop/style/exponential_notation.rb +3 -2
  79. data/lib/rubocop/cop/style/fetch_env_var.rb +32 -6
  80. data/lib/rubocop/cop/style/hash_conversion.rb +16 -8
  81. data/lib/rubocop/cop/style/if_unless_modifier.rb +13 -6
  82. data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
  83. data/lib/rubocop/cop/style/inverse_methods.rb +1 -1
  84. data/lib/rubocop/cop/style/it_assignment.rb +69 -12
  85. data/lib/rubocop/cop/style/it_block_parameter.rb +36 -15
  86. data/lib/rubocop/cop/style/map_to_hash.rb +1 -3
  87. data/lib/rubocop/cop/style/map_to_set.rb +1 -3
  88. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +4 -6
  89. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +16 -0
  90. data/lib/rubocop/cop/style/min_max_comparison.rb +13 -5
  91. data/lib/rubocop/cop/style/parallel_assignment.rb +32 -20
  92. data/lib/rubocop/cop/style/redundant_array_flatten.rb +50 -0
  93. data/lib/rubocop/cop/style/redundant_begin.rb +34 -0
  94. data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
  95. data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -9
  96. data/lib/rubocop/cop/style/redundant_freeze.rb +1 -1
  97. data/lib/rubocop/cop/style/redundant_interpolation.rb +1 -1
  98. data/lib/rubocop/cop/style/redundant_line_continuation.rb +1 -1
  99. data/lib/rubocop/cop/style/redundant_parentheses.rb +42 -6
  100. data/lib/rubocop/cop/style/redundant_self.rb +8 -5
  101. data/lib/rubocop/cop/style/safe_navigation.rb +38 -12
  102. data/lib/rubocop/cop/style/single_line_methods.rb +7 -4
  103. data/lib/rubocop/cop/style/sole_nested_conditional.rb +32 -2
  104. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +1 -1
  105. data/lib/rubocop/cop/style/string_concatenation.rb +1 -1
  106. data/lib/rubocop/cop/style/symbol_array.rb +1 -1
  107. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  108. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  109. data/lib/rubocop/cop/variable_force/variable.rb +1 -1
  110. data/lib/rubocop/cop/variable_force.rb +25 -8
  111. data/lib/rubocop/cops_documentation_generator.rb +1 -0
  112. data/lib/rubocop/formatter/disabled_config_formatter.rb +18 -5
  113. data/lib/rubocop/formatter/fuubar_style_formatter.rb +1 -1
  114. data/lib/rubocop/formatter/markdown_formatter.rb +1 -0
  115. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  116. data/lib/rubocop/formatter/pacman_formatter.rb +1 -0
  117. data/lib/rubocop/lsp/diagnostic.rb +4 -4
  118. data/lib/rubocop/lsp/routes.rb +35 -6
  119. data/lib/rubocop/pending_cops_reporter.rb +56 -0
  120. data/lib/rubocop/result_cache.rb +14 -12
  121. data/lib/rubocop/rspec/expect_offense.rb +9 -3
  122. data/lib/rubocop/runner.rb +6 -4
  123. data/lib/rubocop/server/cache.rb +4 -2
  124. data/lib/rubocop/server/client_command/base.rb +10 -0
  125. data/lib/rubocop/server/client_command/exec.rb +2 -1
  126. data/lib/rubocop/server/client_command/start.rb +11 -1
  127. data/lib/rubocop/target_finder.rb +9 -9
  128. data/lib/rubocop/version.rb +1 -1
  129. data/lib/rubocop.rb +11 -1
  130. data/lib/ruby_lsp/rubocop/addon.rb +2 -2
  131. metadata +17 -7
@@ -44,17 +44,17 @@ module RuboCop
44
44
  class HashConversion < Base
45
45
  extend AutoCorrector
46
46
 
47
- MSG_TO_H = 'Prefer ary.to_h to Hash[ary].'
48
- MSG_LITERAL_MULTI_ARG = 'Prefer literal hash to Hash[arg1, arg2, ...].'
49
- MSG_LITERAL_HASH_ARG = 'Prefer literal hash to Hash[key: value, ...].'
50
- MSG_SPLAT = 'Prefer array_of_pairs.to_h to Hash[*array].'
47
+ MSG_TO_H = 'Prefer `ary.to_h` to `Hash[ary]`.'
48
+ MSG_LITERAL_MULTI_ARG = 'Prefer literal hash to `Hash[arg1, arg2, ...]`.'
49
+ MSG_LITERAL_HASH_ARG = 'Prefer literal hash to `Hash[key: value, ...]`.'
50
+ MSG_SPLAT = 'Prefer `array_of_pairs.to_h` to `Hash[*array]`.'
51
51
  RESTRICT_ON_SEND = %i[[]].freeze
52
52
 
53
53
  # @!method hash_from_array?(node)
54
54
  def_node_matcher :hash_from_array?, '(send (const {nil? cbase} :Hash) :[] ...)'
55
55
 
56
56
  def on_send(node)
57
- return unless hash_from_array?(node)
57
+ return if part_of_ignored_node?(node) || !hash_from_array?(node)
58
58
 
59
59
  # There are several cases:
60
60
  # If there is one argument:
@@ -63,7 +63,8 @@ module RuboCop
63
63
  # If there is 0 or 2+ arguments:
64
64
  # Hash[a1, a2, a3, a4] => {a1 => a2, a3 => a4}
65
65
  # ...but don't suggest correction if there is odd number of them (it is a bug)
66
- node.arguments.count == 1 ? single_argument(node) : multi_argument(node)
66
+ node.arguments.one? ? single_argument(node) : multi_argument(node)
67
+ ignore_node(node)
67
68
  end
68
69
 
69
70
  private
@@ -111,7 +112,12 @@ module RuboCop
111
112
  end
112
113
 
113
114
  def requires_parens?(node)
114
- (node.call_type? && node.arguments.any? && !node.parenthesized?) || node.operator_keyword?
115
+ if node.call_type?
116
+ return false if node.method?(:[])
117
+ return true if node.arguments.any? && !node.parenthesized?
118
+ end
119
+
120
+ node.operator_keyword?
115
121
  end
116
122
 
117
123
  def multi_argument(node)
@@ -122,7 +128,9 @@ module RuboCop
122
128
  corrector.replace(node, args_to_hash(node.arguments))
123
129
 
124
130
  parent = node.parent
125
- add_parentheses(parent, corrector) if parent&.send_type? && !parent.parenthesized?
131
+ if parent&.send_type? && !parent.method?(:to_h) && !parent.parenthesized?
132
+ add_parentheses(parent, corrector)
133
+ end
126
134
  end
127
135
  end
128
136
  end
@@ -132,12 +132,10 @@ module RuboCop
132
132
  end
133
133
 
134
134
  def pattern_matching_nodes(condition)
135
- if condition.type?(:match_pattern, :match_pattern_p)
135
+ if condition.any_match_pattern_type?
136
136
  [condition]
137
137
  else
138
- condition.each_descendant.select do |node|
139
- node.type?(:match_pattern, :match_pattern_p)
140
- end
138
+ condition.each_descendant.select(&:any_match_pattern_type?)
141
139
  end
142
140
  end
143
141
 
@@ -225,8 +223,17 @@ module RuboCop
225
223
 
226
224
  def too_long_line_based_on_allow_uri?(line)
227
225
  if allow_uri?
228
- uri_range = find_excessive_uri_range(line)
229
- return false if uri_range && allowed_uri_position?(line, uri_range)
226
+ uri_range = find_excessive_range(line, :uri)
227
+ return false if uri_range && allowed_position?(line, uri_range)
228
+ end
229
+
230
+ true
231
+ end
232
+
233
+ def too_long_line_based_on_allow_qualified_name?(line)
234
+ if allow_qualified_name?
235
+ namespace_range = find_excessive_range(line, :namespace)
236
+ return false if namespace_range && allowed_position?(line, namespace_range)
230
237
  end
231
238
 
232
239
  true
@@ -68,7 +68,7 @@ module RuboCop
68
68
  end
69
69
 
70
70
  def autocorrect(corrector, node)
71
- if node.type?(:while_post, :until_post)
71
+ if node.post_condition_loop?
72
72
  replace_begin_end_with_modifier(corrector, node)
73
73
  elsif node.modifier_form?
74
74
  replace_source(corrector, node.source_range, modifier_replacement(node))
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Check for usages of not (`not` or `!`) called on a method
6
+ # Checks for usages of not (`not` or `!`) called on a method
7
7
  # when an inverse of that method can be used instead.
8
8
  #
9
9
  # Methods that can be inverted by a not (`not` or `!`) should be defined
@@ -3,33 +3,90 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks for assignments to a local `it` variable inside a block
6
+ # Checks for local variables and method parameters named `it`,
7
7
  # where `it` can refer to the first anonymous parameter as of Ruby 3.4.
8
+ # Use a meaningful variable name instead.
8
9
  #
9
- # Although Ruby allows reassigning `it` in these cases, it could
10
+ # NOTE: Although Ruby allows reassigning `it` in these cases, it could
10
11
  # cause confusion if `it` is used as a block parameter elsewhere.
11
- # For consistency, this also applies to numblocks and blocks with
12
- # parameters, even though `it` cannot be used in those cases.
13
12
  #
14
13
  # @example
15
14
  # # bad
16
- # foo { it = 5 }
17
- # foo { |bar| it = bar }
18
- # foo { it = _2 }
15
+ # it = 5
19
16
  #
20
- # # good - use a different variable name
21
- # foo { var = 5 }
22
- # foo { |bar| var = bar }
23
- # foo { bar = _2 }
17
+ # # good
18
+ # var = 5
19
+ #
20
+ # # bad
21
+ # def foo(it)
22
+ # end
23
+ #
24
+ # # good
25
+ # def foo(arg)
26
+ # end
27
+ #
28
+ # # bad
29
+ # def foo(it = 5)
30
+ # end
31
+ #
32
+ # # good
33
+ # def foo(arg = 5)
34
+ # end
35
+ #
36
+ # # bad
37
+ # def foo(*it)
38
+ # end
39
+ #
40
+ # # good
41
+ # def foo(*args)
42
+ # end
43
+ #
44
+ # # bad
45
+ # def foo(it:)
46
+ # end
47
+ #
48
+ # # good
49
+ # def foo(arg:)
50
+ # end
51
+ #
52
+ # # bad
53
+ # def foo(it: 5)
54
+ # end
55
+ #
56
+ # # good
57
+ # def foo(arg: 5)
58
+ # end
59
+ #
60
+ # # bad
61
+ # def foo(**it)
62
+ # end
63
+ #
64
+ # # good
65
+ # def foo(**kwargs)
66
+ # end
67
+ #
68
+ # # bad
69
+ # def foo(&it)
70
+ # end
71
+ #
72
+ # # good
73
+ # def foo(&block)
74
+ # end
24
75
  class ItAssignment < Base
25
76
  MSG = '`it` is the default block parameter; consider another name.'
26
77
 
27
78
  def on_lvasgn(node)
28
79
  return unless node.name == :it
29
- return unless node.each_ancestor(:any_block).any?
30
80
 
31
81
  add_offense(node.loc.name)
32
82
  end
83
+ alias on_arg on_lvasgn
84
+ alias on_optarg on_lvasgn
85
+ alias on_restarg on_lvasgn
86
+ alias on_blockarg on_lvasgn
87
+ alias on_kwarg on_lvasgn
88
+ alias on_kwoptarg on_lvasgn
89
+ alias on_kwrestarg on_lvasgn
33
90
  end
34
91
  end
35
92
  end
@@ -5,15 +5,28 @@ module RuboCop
5
5
  module Style
6
6
  # Checks for blocks with one argument where `it` block parameter can be used.
7
7
  #
8
- # It provides three `EnforcedStyle` options:
8
+ # It provides four `EnforcedStyle` options:
9
9
  #
10
- # 1. `only_numbered_parameters` (default) ... Detects only numbered block parameters.
11
- # 2. `always` ... Always uses the `it` block parameter.
12
- # 3. `disallow` ... Disallows the `it` block parameter.
10
+ # 1. `allow_single_line` (default) ... Always uses the `it` block parameter in a single line.
11
+ # 2. `only_numbered_parameters` ... Detects only numbered block parameters.
12
+ # 3. `always` ... Always uses the `it` block parameter.
13
+ # 4. `disallow` ... Disallows the `it` block parameter.
13
14
  #
14
- # A single numbered parameter is detected when `only_numbered_parameters` or `always`.
15
+ # A single numbered parameter is detected when `allow_single_line`,
16
+ # `only_numbered_parameters`, or `always`.
15
17
  #
16
- # @example EnforcedStyle: only_numbered_parameters (default)
18
+ # @example EnforcedStyle: allow_single_line (default)
19
+ # # bad
20
+ # block do
21
+ # do_something(it)
22
+ # end
23
+ # block { do_something(_1) }
24
+ #
25
+ # # good
26
+ # block { do_something(it) }
27
+ # block { |named_param| do_something(named_param) }
28
+ #
29
+ # @example EnforcedStyle: only_numbered_parameters
17
30
  # # bad
18
31
  # block { do_something(_1) }
19
32
  #
@@ -42,8 +55,9 @@ module RuboCop
42
55
  extend TargetRubyVersion
43
56
  extend AutoCorrector
44
57
 
45
- MSG_USE_IT_BLOCK_PARAMETER = 'Use `it` block parameter.'
46
- MSG_AVOID_IT_BLOCK_PARAMETER = 'Avoid using `it` block parameter.'
58
+ MSG_USE_IT_PARAMETER = 'Use `it` block parameter.'
59
+ MSG_AVOID_IT_PARAMETER = 'Avoid using `it` block parameter.'
60
+ MSG_AVOID_IT_PARAMETER_MULTILINE = 'Avoid using `it` block parameter for multi-line blocks.'
47
61
 
48
62
  minimum_target_ruby_version 3.4
49
63
 
@@ -57,7 +71,7 @@ module RuboCop
57
71
  variables = find_block_variables(node, node.first_argument.source)
58
72
 
59
73
  variables.each do |variable|
60
- add_offense(variable, message: MSG_USE_IT_BLOCK_PARAMETER) do |corrector|
74
+ add_offense(variable, message: MSG_USE_IT_PARAMETER) do |corrector|
61
75
  corrector.remove(node.arguments)
62
76
  corrector.replace(variable, 'it')
63
77
  end
@@ -71,26 +85,33 @@ module RuboCop
71
85
  variables = find_block_variables(node, '_1')
72
86
 
73
87
  variables.each do |variable|
74
- add_offense(variable, message: MSG_USE_IT_BLOCK_PARAMETER) do |corrector|
88
+ add_offense(variable, message: MSG_USE_IT_PARAMETER) do |corrector|
75
89
  corrector.replace(variable, 'it')
76
90
  end
77
91
  end
78
92
  end
79
93
 
80
94
  def on_itblock(node)
81
- return unless style == :disallow
95
+ case style
96
+ when :allow_single_line
97
+ return if node.single_line?
82
98
 
83
- variables = find_block_variables(node, 'it')
99
+ add_offense(node, message: MSG_AVOID_IT_PARAMETER_MULTILINE)
100
+ when :disallow
101
+ variables = find_block_variables(node, 'it')
84
102
 
85
- variables.each do |variable|
86
- add_offense(variable, message: MSG_AVOID_IT_BLOCK_PARAMETER)
103
+ variables.each do |variable|
104
+ add_offense(variable, message: MSG_AVOID_IT_PARAMETER)
105
+ end
87
106
  end
88
107
  end
89
108
 
90
109
  private
91
110
 
92
111
  def find_block_variables(node, block_argument_name)
93
- node.each_descendant(:lvar).select do |descendant|
112
+ return [] unless node.body
113
+
114
+ node.body.each_descendant(:lvar).select do |descendant|
94
115
  descendant.source == block_argument_name
95
116
  end
96
117
  end
@@ -56,12 +56,10 @@ module RuboCop
56
56
 
57
57
  def on_send(node)
58
58
  return unless (to_h_node, map_node = map_to_h(node))
59
+ return if to_h_node.block_literal?
59
60
 
60
61
  message = format(MSG, method: map_node.loc.selector.source, dot: to_h_node.loc.dot.source)
61
62
  add_offense(map_node.loc.selector, message: message) do |corrector|
62
- # If the `to_h` call already has a block, do not autocorrect.
63
- next if to_h_node.block_literal?
64
-
65
63
  autocorrect(corrector, to_h_node, map_node)
66
64
  end
67
65
  end
@@ -40,12 +40,10 @@ module RuboCop
40
40
 
41
41
  def on_send(node)
42
42
  return unless (to_set_node, map_node = map_to_set?(node))
43
+ return if to_set_node.block_literal?
43
44
 
44
45
  message = format(MSG, method: map_node.loc.selector.source)
45
46
  add_offense(map_node.loc.selector, message: message) do |corrector|
46
- # If the `to_set` call already has a block, do not autocorrect.
47
- next if to_set_node.block_literal?
48
-
49
47
  autocorrect(corrector, to_set_node, map_node)
50
48
  end
51
49
  end
@@ -167,7 +167,7 @@ module RuboCop
167
167
  def call_in_match_pattern?(node)
168
168
  return false unless (parent = node.parent)
169
169
 
170
- parent.type?(:match_pattern, :match_pattern_p)
170
+ parent.any_match_pattern_type?
171
171
  end
172
172
 
173
173
  def hash_literal_in_arguments?(node)
@@ -222,11 +222,9 @@ module RuboCop
222
222
  end
223
223
 
224
224
  def unary_literal?(node)
225
- # NOTE: should be removed after releasing https://github.com/rubocop/rubocop-ast/pull/379
226
- return node.source.match?(/\A[+-]/) if node.complex_type?
225
+ return true if node.numeric_type? && node.sign?
227
226
 
228
- (node.numeric_type? && node.sign?) ||
229
- (node.parent&.send_type? && node.parent.unary_operation?)
227
+ node.parent&.send_type? && node.parent.unary_operation?
230
228
  end
231
229
 
232
230
  def assigned_before?(node, target)
@@ -251,7 +249,7 @@ module RuboCop
251
249
  return false unless (last_argument = node.last_argument)
252
250
  return true if last_argument.forwarded_restarg_type?
253
251
 
254
- last_argument.hash_type? && last_argument.children.first&.forwarded_kwrestarg_type?
252
+ last_argument.hash_type? && last_argument.children.any?(&:forwarded_kwrestarg_type?)
255
253
  end
256
254
  end
257
255
  # rubocop:enable Metrics/ModuleLength, Metrics/CyclomaticComplexity
@@ -132,6 +132,22 @@ module RuboCop
132
132
  # bar :baz
133
133
  # end
134
134
  #
135
+ # @example AllowedMethods: ["puts", "print"]
136
+ #
137
+ # # good
138
+ # puts "Hello world"
139
+ # print "Hello world"
140
+ # # still enforces parentheses on other methods
141
+ # array.delete(e)
142
+ #
143
+ # @example AllowedPatterns: ["^assert"]
144
+ #
145
+ # # good
146
+ # assert_equal 'test', x
147
+ # assert_match(/foo/, bar)
148
+ # # still enforces parentheses on other methods
149
+ # array.delete(e)
150
+ #
135
151
  # @example AllowParenthesesInMultilineCall: false (default)
136
152
  #
137
153
  # # bad
@@ -39,13 +39,21 @@ module RuboCop
39
39
  include RangeHelp
40
40
 
41
41
  MSG = 'Use `%<prefer>s` instead.'
42
- GRATER_OPERATORS = %i[> >=].freeze
42
+ GREATER_OPERATORS = %i[> >=].freeze
43
43
  LESS_OPERATORS = %i[< <=].freeze
44
- COMPARISON_OPERATORS = GRATER_OPERATORS + LESS_OPERATORS
44
+ COMPARISON_OPERATORS = (GREATER_OPERATORS + LESS_OPERATORS).to_set.freeze
45
+
46
+ # @!method comparison_condition(node, name)
47
+ def_node_matcher :comparison_condition, <<~PATTERN
48
+ {
49
+ (send $_lhs $COMPARISON_OPERATORS $_rhs)
50
+ (begin (send $_lhs $COMPARISON_OPERATORS $_rhs))
51
+ }
52
+ PATTERN
45
53
 
46
54
  def on_if(node)
47
- lhs, operator, rhs = *node.condition
48
- return unless COMPARISON_OPERATORS.include?(operator)
55
+ lhs, operator, rhs = comparison_condition(node.condition)
56
+ return unless operator
49
57
 
50
58
  if_branch = node.if_branch
51
59
  else_branch = node.else_branch
@@ -63,7 +71,7 @@ module RuboCop
63
71
 
64
72
  def preferred_method(operator, lhs, rhs, if_branch, else_branch)
65
73
  if lhs == if_branch && rhs == else_branch
66
- GRATER_OPERATORS.include?(operator) ? 'max' : 'min'
74
+ GREATER_OPERATORS.include?(operator) ? 'max' : 'min'
67
75
  elsif lhs == else_branch && rhs == if_branch
68
76
  LESS_OPERATORS.include?(operator) ? 'max' : 'min'
69
77
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'tsort'
4
-
5
3
  module RuboCop
6
4
  module Cop
7
5
  module Style
@@ -29,6 +27,8 @@ module RuboCop
29
27
  MSG = 'Do not use parallel assignment.'
30
28
 
31
29
  def on_masgn(node) # rubocop:disable Metrics/AbcSize
30
+ return if part_of_ignored_node?(node)
31
+
32
32
  rhs = node.rhs
33
33
  rhs = rhs.body if rhs.rescue_type?
34
34
  rhs_elements = Array(rhs).compact # edge case for one constant
@@ -41,6 +41,7 @@ module RuboCop
41
41
  add_offense(range) do |corrector|
42
42
  autocorrect(corrector, node, rhs)
43
43
  end
44
+ ignore_node(node)
44
45
  end
45
46
 
46
47
  private
@@ -91,15 +92,9 @@ module RuboCop
91
92
  def find_valid_order(left_elements, right_elements)
92
93
  # arrange left_elements in an order such that no corresponding right
93
94
  # element refers to a left element earlier in the sequence
94
- # this can be done using an algorithm called a "topological sort"
95
- # fortunately for us, Ruby's stdlib contains an implementation
96
95
  assignments = left_elements.zip(right_elements)
97
96
 
98
- begin
99
- AssignmentSorter.new(assignments).tsort
100
- rescue TSort::Cyclic
101
- nil
102
- end
97
+ AssignmentSorter.new(assignments).tsort
103
98
  end
104
99
 
105
100
  # Converts (send nil :something) nodes to (send (:self) :something).
@@ -114,10 +109,9 @@ module RuboCop
114
109
  # @!method implicit_self_getter?(node)
115
110
  def_node_matcher :implicit_self_getter?, '(send nil? $_)'
116
111
 
117
- # Helper class necessitated by silly design of TSort prior to Ruby 2.1
118
- # Newer versions have a better API, but that doesn't help us
112
+ # Topologically sorts the assignments with Kahn's algorithm.
113
+ # https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm
119
114
  class AssignmentSorter
120
- include TSort
121
115
  extend RuboCop::NodePattern::Macros
122
116
 
123
117
  # @!method var_name(node)
@@ -133,21 +127,39 @@ module RuboCop
133
127
  @assignments = assignments
134
128
  end
135
129
 
136
- def tsort_each_node(...)
137
- @assignments.each(...)
130
+ def tsort
131
+ dependencies = @assignments.to_h do |assignment|
132
+ [assignment, dependencies_for_assignment(assignment)]
133
+ end
134
+ result = []
135
+
136
+ while (matched_node, = dependencies.find { |_node, edges| edges.empty? })
137
+ dependencies.delete(matched_node)
138
+ result.push(matched_node)
139
+
140
+ dependencies.each do |node, edges|
141
+ dependencies[node].delete(matched_node) if edges.include?(matched_node)
142
+ end
143
+ end
144
+ # Cyclic dependency
145
+ return nil if dependencies.any?
146
+
147
+ result
138
148
  end
139
149
 
140
- def tsort_each_child(assignment)
141
- # yield all the assignments which must come after `assignment`
142
- # (due to dependencies on the previous value of the assigned var)
150
+ # Returns all the assignments which must come after `assignment`
151
+ # (due to dependencies on the previous value of the assigned var)
152
+ def dependencies_for_assignment(assignment)
143
153
  my_lhs, _my_rhs = *assignment
144
154
 
145
- @assignments.each do |other|
146
- _other_lhs, other_rhs = *other
155
+ @assignments.filter_map do |other|
156
+ # Exclude self, there are no dependencies in cases such as `a, b = a, b`.
157
+ next if other == assignment
147
158
 
159
+ _other_lhs, other_rhs = *other
148
160
  next unless dependency?(my_lhs, other_rhs)
149
161
 
150
- yield other
162
+ other
151
163
  end
152
164
  end
153
165
 
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for redundant calls of `Array#flatten`.
7
+ #
8
+ # `Array#join` joins nested arrays recursively, so flattening an array
9
+ # beforehand is redundant.
10
+ #
11
+ # @safety
12
+ # Cop is unsafe because the receiver of `flatten` method might not
13
+ # be an `Array`, so it's possible it won't respond to `join` method,
14
+ # or the end result would be different.
15
+ # Also, if the global variable `$,` is set to a value other than the default `nil`,
16
+ # false positives may occur.
17
+ #
18
+ # @example
19
+ # # bad
20
+ # x.flatten.join
21
+ # x.flatten(1).join
22
+ #
23
+ # # good
24
+ # x.join
25
+ #
26
+ class RedundantArrayFlatten < Base
27
+ extend AutoCorrector
28
+
29
+ MSG = 'Remove the redundant `flatten`.'
30
+
31
+ RESTRICT_ON_SEND = %i[flatten].freeze
32
+
33
+ # @!method flatten_join?(node)
34
+ def_node_matcher :flatten_join?, <<~PATTERN
35
+ (call (call !nil? :flatten _?) :join (nil)?)
36
+ PATTERN
37
+
38
+ def on_send(node)
39
+ return unless flatten_join?(node.parent)
40
+
41
+ range = node.loc.dot.begin.join(node.source_range.end)
42
+ add_offense(range) do |corrector|
43
+ corrector.remove(range)
44
+ end
45
+ end
46
+ alias on_csend on_send
47
+ end
48
+ end
49
+ end
50
+ end
@@ -85,6 +85,29 @@ module RuboCop
85
85
  end
86
86
  alias on_defs on_def
87
87
 
88
+ def on_if(node)
89
+ return if node.modifier_form?
90
+
91
+ inspect_branches(node)
92
+ end
93
+
94
+ def on_case(node)
95
+ inspect_branches(node)
96
+ end
97
+ alias on_case_match on_case
98
+
99
+ def on_while(node)
100
+ return if node.modifier_form?
101
+
102
+ body = node.body
103
+
104
+ return unless body&.kwbegin_type?
105
+ return if body.rescue_node || body.ensure_node
106
+
107
+ register_offense(body)
108
+ end
109
+ alias on_until on_while
110
+
88
111
  def on_block(node)
89
112
  return if target_ruby_version < 2.5
90
113
  return if node.send_node.lambda_literal?
@@ -180,6 +203,8 @@ module RuboCop
180
203
  end
181
204
 
182
205
  def begin_block_has_multiline_statements?(node)
206
+ return false unless node.parent
207
+
183
208
  node.children.count >= 2
184
209
  end
185
210
 
@@ -199,6 +224,15 @@ module RuboCop
199
224
  def valid_begin_assignment?(node)
200
225
  node.parent&.assignment? && !node.children.one?
201
226
  end
227
+
228
+ def inspect_branches(node)
229
+ node.branches.each do |branch|
230
+ next unless branch&.kwbegin_type?
231
+ next if branch.rescue_node || branch.ensure_node
232
+
233
+ register_offense(branch)
234
+ end
235
+ end
202
236
  end
203
237
  end
204
238
  end
@@ -247,7 +247,7 @@ module RuboCop
247
247
  "#{if_branch.receiver.source} #{if_branch.method_name} (#{argument_source}"
248
248
  elsif if_branch.true_type?
249
249
  condition = if_branch.parent.condition
250
- return condition.source if condition.arguments.empty?
250
+ return condition.source if condition.arguments.empty? || condition.parenthesized?
251
251
 
252
252
  wrap_arguments_with_parens(condition)
253
253
  else
@@ -49,7 +49,7 @@ module RuboCop
49
49
  (block
50
50
  $(call _ :fetch _)
51
51
  (args)
52
- ${nil? #basic_literal? #const_type?})
52
+ ${nil? basic_literal? const_type?})
53
53
  PATTERN
54
54
 
55
55
  def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
@@ -71,14 +71,6 @@ module RuboCop
71
71
 
72
72
  private
73
73
 
74
- def basic_literal?(node)
75
- node&.basic_literal?
76
- end
77
-
78
- def const_type?(node)
79
- node&.const_type?
80
- end
81
-
82
74
  def should_not_check?(send, body)
83
75
  (body&.const_type? && !check_for_constant?) ||
84
76
  (body&.str_type? && !check_for_string?) ||