rubocop 1.75.5 → 1.77.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 (89) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +20 -14
  3. data/config/default.yml +74 -7
  4. data/config/obsoletion.yml +6 -3
  5. data/lib/rubocop/cop/autocorrect_logic.rb +18 -10
  6. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -1
  7. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +5 -2
  8. data/lib/rubocop/cop/gemspec/attribute_assignment.rb +91 -0
  9. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +37 -15
  10. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
  11. data/lib/rubocop/cop/gemspec/require_mfa.rb +15 -1
  12. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +4 -4
  13. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +1 -0
  14. data/lib/rubocop/cop/layout/class_structure.rb +35 -0
  15. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +1 -1
  16. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +7 -3
  17. data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -1
  18. data/lib/rubocop/cop/layout/line_length.rb +26 -5
  19. data/lib/rubocop/cop/layout/space_before_brackets.rb +5 -38
  20. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +8 -2
  21. data/lib/rubocop/cop/lint/ambiguous_range.rb +5 -0
  22. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +1 -1
  23. data/lib/rubocop/cop/lint/duplicate_methods.rb +84 -2
  24. data/lib/rubocop/cop/lint/empty_interpolation.rb +3 -1
  25. data/lib/rubocop/cop/lint/float_comparison.rb +31 -4
  26. data/lib/rubocop/cop/lint/identity_comparison.rb +19 -15
  27. data/lib/rubocop/cop/lint/literal_as_condition.rb +19 -27
  28. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
  29. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +4 -4
  30. data/lib/rubocop/cop/lint/self_assignment.rb +25 -0
  31. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +5 -0
  32. data/lib/rubocop/cop/lint/useless_access_modifier.rb +29 -4
  33. data/lib/rubocop/cop/lint/useless_assignment.rb +2 -0
  34. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +90 -0
  35. data/lib/rubocop/cop/lint/useless_or.rb +98 -0
  36. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +3 -3
  37. data/lib/rubocop/cop/metrics/abc_size.rb +1 -1
  38. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  39. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -1
  40. data/lib/rubocop/cop/mixin/gemspec_help.rb +22 -0
  41. data/lib/rubocop/cop/mixin/line_length_help.rb +24 -8
  42. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
  43. data/lib/rubocop/cop/naming/file_name.rb +2 -2
  44. data/lib/rubocop/cop/naming/predicate_method.rb +281 -0
  45. data/lib/rubocop/cop/naming/{predicate_name.rb → predicate_prefix.rb} +4 -4
  46. data/lib/rubocop/cop/style/access_modifier_declarations.rb +32 -10
  47. data/lib/rubocop/cop/style/case_like_if.rb +1 -1
  48. data/lib/rubocop/cop/style/collection_querying.rb +167 -0
  49. data/lib/rubocop/cop/style/command_literal.rb +1 -1
  50. data/lib/rubocop/cop/style/comparable_between.rb +3 -0
  51. data/lib/rubocop/cop/style/conditional_assignment.rb +3 -1
  52. data/lib/rubocop/cop/style/data_inheritance.rb +7 -0
  53. data/lib/rubocop/cop/style/def_with_parentheses.rb +18 -5
  54. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +100 -0
  55. data/lib/rubocop/cop/style/exponential_notation.rb +2 -2
  56. data/lib/rubocop/cop/style/fetch_env_var.rb +32 -6
  57. data/lib/rubocop/cop/style/hash_conversion.rb +12 -3
  58. data/lib/rubocop/cop/style/if_unless_modifier.rb +33 -6
  59. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +4 -7
  60. data/lib/rubocop/cop/style/it_block_parameter.rb +33 -14
  61. data/lib/rubocop/cop/style/map_to_hash.rb +11 -0
  62. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -2
  63. data/lib/rubocop/cop/style/min_max_comparison.rb +13 -5
  64. data/lib/rubocop/cop/style/multiline_if_modifier.rb +2 -0
  65. data/lib/rubocop/cop/style/percent_q_literals.rb +1 -1
  66. data/lib/rubocop/cop/style/redundant_array_flatten.rb +50 -0
  67. data/lib/rubocop/cop/style/redundant_format.rb +6 -1
  68. data/lib/rubocop/cop/style/redundant_interpolation.rb +1 -1
  69. data/lib/rubocop/cop/style/redundant_parentheses.rb +26 -5
  70. data/lib/rubocop/cop/style/redundant_self.rb +8 -5
  71. data/lib/rubocop/cop/style/regexp_literal.rb +1 -1
  72. data/lib/rubocop/cop/style/safe_navigation.rb +24 -11
  73. data/lib/rubocop/cop/style/sole_nested_conditional.rb +6 -3
  74. data/lib/rubocop/cop/style/string_concatenation.rb +1 -2
  75. data/lib/rubocop/cop/style/struct_inheritance.rb +8 -1
  76. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  77. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  78. data/lib/rubocop/cop/team.rb +1 -1
  79. data/lib/rubocop/cop/variable_force/assignment.rb +7 -3
  80. data/lib/rubocop/formatter/disabled_config_formatter.rb +1 -0
  81. data/lib/rubocop/formatter/fuubar_style_formatter.rb +1 -1
  82. data/lib/rubocop/formatter/html_formatter.rb +1 -1
  83. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  84. data/lib/rubocop/lsp/diagnostic.rb +4 -4
  85. data/lib/rubocop/rspec/expect_offense.rb +9 -3
  86. data/lib/rubocop/version.rb +1 -1
  87. data/lib/rubocop.rb +8 -1
  88. data/lib/ruby_lsp/rubocop/addon.rb +2 -2
  89. metadata +15 -11
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for empty strings being assigned inside string interpolation.
7
+ #
8
+ # Empty strings are a meaningless outcome inside of string interpolation, so we remove them.
9
+ # Alternatively, when configured to do so, we prioritise using empty strings.
10
+ #
11
+ # While this cop would also apply to variables that are only going to be used as strings,
12
+ # RuboCop can't detect that, so we only check inside of string interpolation.
13
+ #
14
+ # @example EnforcedStyle: trailing_conditional (default)
15
+ # # bad
16
+ # "#{condition ? 'foo' : ''}"
17
+ #
18
+ # # good
19
+ # "#{'foo' if condition}"
20
+ #
21
+ # # bad
22
+ # "#{condition ? '' : 'foo'}"
23
+ #
24
+ # # good
25
+ # "#{'foo' unless condition}"
26
+ #
27
+ # @example EnforcedStyle: ternary
28
+ # # bad
29
+ # "#{'foo' if condition}"
30
+ #
31
+ # # good
32
+ # "#{condition ? 'foo' : ''}"
33
+ #
34
+ # # bad
35
+ # "#{'foo' unless condition}"
36
+ #
37
+ # # good
38
+ # "#{condition ? '' : 'foo'}"
39
+ #
40
+ class EmptyStringInsideInterpolation < Base
41
+ include ConfigurableEnforcedStyle
42
+ include Interpolation
43
+ extend AutoCorrector
44
+
45
+ MSG_TRAILING_CONDITIONAL = 'Do not use trailing conditionals in string interpolation.'
46
+ MSG_TERNARY = 'Do not return empty strings in string interpolation.'
47
+
48
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
49
+ def on_interpolation(node)
50
+ node.each_child_node(:if) do |child_node|
51
+ if style == :trailing_conditional
52
+ if empty_if_outcome?(child_node)
53
+ ternary_style_autocorrect(child_node, child_node.else_branch.source, 'unless')
54
+ end
55
+
56
+ if empty_else_outcome?(child_node)
57
+ ternary_style_autocorrect(child_node, child_node.if_branch.source, 'if')
58
+ end
59
+ elsif style == :ternary
60
+ next unless child_node.modifier_form?
61
+
62
+ ternary_component = if child_node.unless?
63
+ "'' : #{child_node.if_branch.source}"
64
+ else
65
+ "#{child_node.if_branch.source} : ''"
66
+ end
67
+
68
+ add_offense(node, message: MSG_TRAILING_CONDITIONAL) do |corrector|
69
+ corrector.replace(node, "\#{#{child_node.condition.source} ? #{ternary_component}}")
70
+ end
71
+ end
72
+ end
73
+ end
74
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
75
+
76
+ private
77
+
78
+ def empty_if_outcome?(node)
79
+ empty_branch_outcome?(node.if_branch)
80
+ end
81
+
82
+ def empty_else_outcome?(node)
83
+ empty_branch_outcome?(node.else_branch)
84
+ end
85
+
86
+ def empty_branch_outcome?(branch)
87
+ return false unless branch
88
+
89
+ branch.nil_type? || (branch.str_type? && branch.value.empty?)
90
+ end
91
+
92
+ def ternary_style_autocorrect(node, outcome, condition)
93
+ add_offense(node, message: MSG_TERNARY) do |corrector|
94
+ corrector.replace(node, "#{outcome} #{condition} #{node.condition.source}")
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -87,7 +87,7 @@ module RuboCop
87
87
  true
88
88
  end
89
89
 
90
- def integral(node)
90
+ def integral?(node)
91
91
  mantissa, = node.source.split('e')
92
92
  /^-?[1-9](\d*[1-9])?$/.match?(mantissa)
93
93
  end
@@ -101,7 +101,7 @@ module RuboCop
101
101
  when :engineering
102
102
  !engineering?(node)
103
103
  when :integral
104
- !integral(node)
104
+ !integral?(node)
105
105
  else
106
106
  false
107
107
  end
@@ -9,7 +9,20 @@ module RuboCop
9
9
  # On the other hand, `ENV.fetch` raises `KeyError` or returns the explicitly
10
10
  # specified default value.
11
11
  #
12
- # @example
12
+ # @example DefaultToNil: true (default)
13
+ # # bad
14
+ # ENV['X']
15
+ # x = ENV['X']
16
+ #
17
+ # # good
18
+ # ENV.fetch('X', nil)
19
+ # x = ENV.fetch('X', nil)
20
+ #
21
+ # # also good
22
+ # !ENV['X']
23
+ # ENV['X'].some_method # (e.g. `.nil?`)
24
+ #
25
+ # @example DefaultToNil: false
13
26
  # # bad
14
27
  # ENV['X']
15
28
  # x = ENV['X']
@@ -25,7 +38,8 @@ module RuboCop
25
38
  class FetchEnvVar < Base
26
39
  extend AutoCorrector
27
40
 
28
- MSG = 'Use `ENV.fetch(%<key>s)` or `ENV.fetch(%<key>s, nil)` instead of `ENV[%<key>s]`.'
41
+ MSG_WITH_NIL = 'Use `ENV.fetch(%<key>s, nil)` instead of `ENV[%<key>s]`.'
42
+ MSG_WITHOUT_NIL = 'Use `ENV.fetch(%<key>s)` instead of `ENV[%<key>s]`.'
29
43
  RESTRICT_ON_SEND = [:[]].freeze
30
44
 
31
45
  # @!method env_with_bracket?(node)
@@ -37,7 +51,7 @@ module RuboCop
37
51
  env_with_bracket?(node) do |name_node|
38
52
  break unless offensive?(node)
39
53
 
40
- message = format(MSG, key: name_node.source)
54
+ message = format(offense_message, key: name_node.source)
41
55
  add_offense(node, message: message) do |corrector|
42
56
  corrector.replace(node, new_code(name_node))
43
57
  end
@@ -46,6 +60,14 @@ module RuboCop
46
60
 
47
61
  private
48
62
 
63
+ def default_to_nil?
64
+ cop_config.fetch('DefaultToNil', true)
65
+ end
66
+
67
+ def offense_message
68
+ default_to_nil? ? MSG_WITH_NIL : MSG_WITHOUT_NIL
69
+ end
70
+
49
71
  def allowed_var?(node)
50
72
  env_key_node = node.children.last
51
73
  env_key_node.str_type? && cop_config['AllowedVars'].include?(env_key_node.value)
@@ -53,12 +75,12 @@ module RuboCop
53
75
 
54
76
  def used_as_flag?(node)
55
77
  return false if node.root?
56
- return true if used_if_condition_in_body(node)
78
+ return true if used_if_condition_in_body?(node)
57
79
 
58
80
  node.parent.send_type? && (node.parent.prefix_bang? || node.parent.comparison_method?)
59
81
  end
60
82
 
61
- def used_if_condition_in_body(node)
83
+ def used_if_condition_in_body?(node)
62
84
  if_node = node.ancestors.find(&:if_type?)
63
85
 
64
86
  return false unless (condition = if_node&.condition)
@@ -125,7 +147,11 @@ module RuboCop
125
147
  end
126
148
 
127
149
  def new_code(name_node)
128
- "ENV.fetch(#{name_node.source}, nil)"
150
+ if default_to_nil?
151
+ "ENV.fetch(#{name_node.source}, nil)"
152
+ else
153
+ "ENV.fetch(#{name_node.source})"
154
+ end
129
155
  end
130
156
  end
131
157
  end
@@ -54,7 +54,7 @@ module RuboCop
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,11 +63,12 @@ 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
67
  end
68
68
 
69
69
  private
70
70
 
71
+ # rubocop:disable Metrics/MethodLength
71
72
  def single_argument(node)
72
73
  first_argument = node.first_argument
73
74
  if first_argument.hash_type?
@@ -82,8 +83,11 @@ module RuboCop
82
83
  replacement = "(#{replacement})" if requires_parens?(first_argument)
83
84
  corrector.replace(node, "#{replacement}.to_h")
84
85
  end
86
+
87
+ ignore_node(node)
85
88
  end
86
89
  end
90
+ # rubocop:enable Metrics/MethodLength
87
91
 
88
92
  def use_zip_method_without_argument?(first_argument)
89
93
  return false unless first_argument&.send_type?
@@ -111,7 +115,12 @@ module RuboCop
111
115
  end
112
116
 
113
117
  def requires_parens?(node)
114
- (node.call_type? && node.arguments.any? && !node.parenthesized?) || node.operator_keyword?
118
+ if node.call_type?
119
+ return false if node.method?(:[])
120
+ return true if node.arguments.any? && !node.parenthesized?
121
+ end
122
+
123
+ node.operator_keyword?
115
124
  end
116
125
 
117
126
  def multi_argument(node)
@@ -23,6 +23,17 @@ module RuboCop
23
23
  # end
24
24
  # ----
25
25
  #
26
+ # The code `def method_name = body if condition` is considered a bad case by
27
+ # `Style/AmbiguousEndlessMethodDefinition` cop. So, to respect the user's intention to use
28
+ # an endless method definition in the `if` body, the following code is allowed:
29
+ #
30
+ # [source,ruby]
31
+ # ----
32
+ # if condition
33
+ # def method_name = body
34
+ # end
35
+ # ----
36
+ #
26
37
  # NOTE: It is allowed when `defined?` argument has an undefined value,
27
38
  # because using the modifier form causes the following incompatibility:
28
39
  #
@@ -77,10 +88,14 @@ module RuboCop
77
88
  [Style::SoleNestedConditional]
78
89
  end
79
90
 
91
+ # rubocop:disable Metrics/AbcSize
80
92
  def on_if(node)
93
+ return if endless_method?(node.body)
94
+
81
95
  condition = node.condition
82
96
  return if defined_nodes(condition).any? { |n| defined_argument_is_undefined?(node, n) } ||
83
97
  pattern_matching_nodes(condition).any?
98
+
84
99
  return unless (msg = message(node))
85
100
 
86
101
  add_offense(node.loc.keyword, message: format(msg, keyword: node.keyword)) do |corrector|
@@ -90,9 +105,14 @@ module RuboCop
90
105
  ignore_node(node)
91
106
  end
92
107
  end
108
+ # rubocop:enable Metrics/AbcSize
93
109
 
94
110
  private
95
111
 
112
+ def endless_method?(body)
113
+ body&.any_def_type? && body.endless?
114
+ end
115
+
96
116
  def defined_nodes(condition)
97
117
  if condition.defined_type?
98
118
  [condition]
@@ -112,12 +132,10 @@ module RuboCop
112
132
  end
113
133
 
114
134
  def pattern_matching_nodes(condition)
115
- if condition.type?(:match_pattern, :match_pattern_p)
135
+ if condition.any_match_pattern_type?
116
136
  [condition]
117
137
  else
118
- condition.each_descendant.select do |node|
119
- node.type?(:match_pattern, :match_pattern_p)
120
- end
138
+ condition.each_descendant.select(&:any_match_pattern_type?)
121
139
  end
122
140
  end
123
141
 
@@ -205,8 +223,17 @@ module RuboCop
205
223
 
206
224
  def too_long_line_based_on_allow_uri?(line)
207
225
  if allow_uri?
208
- uri_range = find_excessive_uri_range(line)
209
- 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)
210
237
  end
211
238
 
212
239
  true
@@ -28,19 +28,16 @@ module RuboCop
28
28
 
29
29
  MSG = 'Avoid modifier `%<keyword>s` after another conditional.'
30
30
 
31
+ # rubocop:disable Metrics/AbcSize
31
32
  def on_if(node)
32
33
  return unless node.modifier_form? && node.body.if_type?
33
34
 
34
35
  add_offense(node.loc.keyword, message: format(MSG, keyword: node.keyword)) do |corrector|
35
- keyword = node.if? ? 'if' : 'unless'
36
-
37
- corrector.replace(node, <<~RUBY.chop)
38
- #{keyword} #{node.condition.source}
39
- #{node.if_branch.source}
40
- end
41
- RUBY
36
+ corrector.wrap(node.if_branch, "#{node.keyword} #{node.condition.source}\n", "\nend")
37
+ corrector.remove(node.if_branch.source_range.end.join(node.condition.source_range.end))
42
38
  end
43
39
  end
40
+ # rubocop:enable Metrics/AbcSize
44
41
  end
45
42
  end
46
43
  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,19 +85,24 @@ 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
 
@@ -45,6 +45,11 @@ module RuboCop
45
45
  }
46
46
  PATTERN
47
47
 
48
+ # @!method destructuring_argument(node)
49
+ def_node_matcher :destructuring_argument, <<~PATTERN
50
+ (args $(mlhs (arg _)+))
51
+ PATTERN
52
+
48
53
  def self.autocorrect_incompatible_with
49
54
  [Layout::SingleLineBlockChain]
50
55
  end
@@ -73,6 +78,12 @@ module RuboCop
73
78
  corrector.replace(map_dot, to_h.loc.dot.source)
74
79
  end
75
80
  corrector.replace(map.loc.selector, 'to_h')
81
+
82
+ return unless map.parent.block_type?
83
+
84
+ if (argument = destructuring_argument(map.parent.arguments))
85
+ corrector.replace(argument, argument.source[1..-2])
86
+ end
76
87
  end
77
88
  # rubocop:enable Metrics/AbcSize
78
89
  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)
@@ -251,7 +251,7 @@ module RuboCop
251
251
  return false unless (last_argument = node.last_argument)
252
252
  return true if last_argument.forwarded_restarg_type?
253
253
 
254
- last_argument.hash_type? && last_argument.children.first&.forwarded_kwrestarg_type?
254
+ last_argument.hash_type? && last_argument.children.any?(&:forwarded_kwrestarg_type?)
255
255
  end
256
256
  end
257
257
  # rubocop:enable Metrics/ModuleLength, Metrics/CyclomaticComplexity
@@ -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
@@ -23,11 +23,13 @@ module RuboCop
23
23
  'clause in a multiline statement.'
24
24
 
25
25
  def on_if(node)
26
+ return if part_of_ignored_node?(node)
26
27
  return unless node.modifier_form? && node.body.multiline?
27
28
 
28
29
  add_offense(node, message: format(MSG, keyword: node.keyword)) do |corrector|
29
30
  corrector.replace(node, to_normal_if(node))
30
31
  end
32
+ ignore_node(node)
31
33
  end
32
34
 
33
35
  private
@@ -45,7 +45,7 @@ module RuboCop
45
45
  # Report offense only if changing case doesn't change semantics,
46
46
  # i.e., if the string would become dynamic or has special characters.
47
47
  ast = parse(corrected(node.source)).ast
48
- return if node.children != ast.children
48
+ return if node.children != ast&.children
49
49
 
50
50
  add_offense(node.loc.begin) do |corrector|
51
51
  corrector.replace(node, corrected(node.source))
@@ -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
@@ -121,7 +121,12 @@ module RuboCop
121
121
  def register_all_fields_literal(node, string, arguments)
122
122
  return unless all_fields_literal?(string, arguments.dup)
123
123
 
124
- formatted_string = format(string, *argument_values(arguments))
124
+ format_arguments = argument_values(arguments)
125
+ begin
126
+ formatted_string = format(string, *format_arguments)
127
+ rescue ArgumentError
128
+ return
129
+ end
125
130
  replacement = quote(formatted_string, node)
126
131
 
127
132
  add_offense(node, message: message(node, replacement)) do |corrector|
@@ -130,7 +130,7 @@ module RuboCop
130
130
  end
131
131
 
132
132
  def require_parentheses?(node)
133
- node.send_type? && !node.arguments.count.zero? && !node.parenthesized_call?
133
+ node.send_type? && node.arguments.any? && !node.parenthesized_call?
134
134
  end
135
135
  end
136
136
  end