rubocop 1.23.0 → 1.25.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +2 -3
  4. data/config/default.yml +60 -12
  5. data/lib/rubocop/cli/command/auto_genenerate_config.rb +1 -1
  6. data/lib/rubocop/cli/command/init_dotfile.rb +1 -1
  7. data/lib/rubocop/cli/command/show_docs_url.rb +48 -0
  8. data/lib/rubocop/cli/command/suggest_extensions.rb +1 -1
  9. data/lib/rubocop/cli.rb +1 -0
  10. data/lib/rubocop/config_loader_resolver.rb +1 -1
  11. data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -1
  12. data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +1 -1
  13. data/lib/rubocop/cop/correctors/if_then_corrector.rb +55 -0
  14. data/lib/rubocop/cop/documentation.rb +19 -2
  15. data/lib/rubocop/cop/gemspec/require_mfa.rb +8 -10
  16. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +10 -3
  17. data/lib/rubocop/cop/generator.rb +4 -3
  18. data/lib/rubocop/cop/internal_affairs/redundant_method_dispatch_node.rb +47 -0
  19. data/lib/rubocop/cop/internal_affairs/undefined_config.rb +3 -1
  20. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  21. data/lib/rubocop/cop/layout/argument_alignment.rb +36 -9
  22. data/lib/rubocop/cop/layout/comment_indentation.rb +31 -2
  23. data/lib/rubocop/cop/layout/dot_position.rb +4 -0
  24. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +5 -1
  25. data/lib/rubocop/cop/layout/hash_alignment.rb +7 -2
  26. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +1 -1
  27. data/lib/rubocop/cop/layout/space_after_colon.rb +1 -1
  28. data/lib/rubocop/cop/layout/space_before_first_arg.rb +4 -0
  29. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +5 -1
  30. data/lib/rubocop/cop/lint/constant_definition_in_block.rb +1 -1
  31. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +16 -4
  32. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +6 -0
  33. data/lib/rubocop/cop/lint/each_with_object_argument.rb +1 -1
  34. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +10 -5
  35. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +7 -4
  36. data/lib/rubocop/cop/metrics/block_length.rb +1 -0
  37. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +0 -9
  38. data/lib/rubocop/cop/metrics/method_length.rb +1 -0
  39. data/lib/rubocop/cop/metrics/module_length.rb +1 -1
  40. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  41. data/lib/rubocop/cop/metrics/utils/repeated_attribute_discount.rb +1 -1
  42. data/lib/rubocop/cop/mixin/enforce_superclass.rb +5 -0
  43. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +4 -3
  44. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +82 -0
  45. data/lib/rubocop/cop/naming/block_forwarding.rb +121 -0
  46. data/lib/rubocop/cop/naming/method_parameter_name.rb +1 -1
  47. data/lib/rubocop/cop/security/open.rb +11 -1
  48. data/lib/rubocop/cop/style/character_literal.rb +8 -1
  49. data/lib/rubocop/cop/style/collection_compact.rb +31 -13
  50. data/lib/rubocop/cop/style/combinable_loops.rb +2 -2
  51. data/lib/rubocop/cop/style/empty_case_condition.rb +10 -0
  52. data/lib/rubocop/cop/style/file_read.rb +112 -0
  53. data/lib/rubocop/cop/style/file_write.rb +124 -0
  54. data/lib/rubocop/cop/style/hash_conversion.rb +2 -1
  55. data/lib/rubocop/cop/style/hash_syntax.rb +36 -0
  56. data/lib/rubocop/cop/style/hash_transform_keys.rb +6 -6
  57. data/lib/rubocop/cop/style/hash_transform_values.rb +6 -6
  58. data/lib/rubocop/cop/style/if_inside_else.rb +15 -0
  59. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +1 -1
  60. data/lib/rubocop/cop/style/map_to_hash.rb +68 -0
  61. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +20 -0
  62. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +3 -1
  63. data/lib/rubocop/cop/style/method_def_parentheses.rb +17 -13
  64. data/lib/rubocop/cop/style/numeric_literals.rb +10 -1
  65. data/lib/rubocop/cop/style/one_line_conditional.rb +18 -39
  66. data/lib/rubocop/cop/style/redundant_begin.rb +2 -6
  67. data/lib/rubocop/cop/style/redundant_interpolation.rb +17 -3
  68. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +5 -1
  69. data/lib/rubocop/cop/style/redundant_self.rb +1 -1
  70. data/lib/rubocop/cop/style/safe_navigation.rb +1 -5
  71. data/lib/rubocop/cop/style/sample.rb +5 -3
  72. data/lib/rubocop/cop/style/single_line_block_params.rb +2 -2
  73. data/lib/rubocop/cop/style/sole_nested_conditional.rb +3 -1
  74. data/lib/rubocop/cop/style/swap_values.rb +2 -0
  75. data/lib/rubocop/cop/style/ternary_parentheses.rb +16 -2
  76. data/lib/rubocop/cop/team.rb +1 -1
  77. data/lib/rubocop/cop/util.rb +9 -1
  78. data/lib/rubocop/formatter/disabled_config_formatter.rb +16 -2
  79. data/lib/rubocop/options.rb +6 -1
  80. data/lib/rubocop/remote_config.rb +1 -3
  81. data/lib/rubocop/result_cache.rb +1 -1
  82. data/lib/rubocop/version.rb +1 -1
  83. data/lib/rubocop.rb +7 -0
  84. metadata +15 -7
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop looks for uses of `map.to_h` or `collect.to_h` that could be
7
+ # written with just `to_h` in Ruby >= 2.6.
8
+ #
9
+ # NOTE: `Style/HashTransformKeys` and `Style/HashTransformValues` will
10
+ # also change this pattern if only hash keys or hash values are being
11
+ # transformed.
12
+ #
13
+ # @safety
14
+ # This cop is unsafe, as it can produce false positives if the receiver
15
+ # is not an `Enumerable`.
16
+ #
17
+ # @example
18
+ # # bad
19
+ # something.map { |v| [v, v * 2] }.to_h
20
+ #
21
+ # # good
22
+ # something.to_h { |v| [v, v * 2] }
23
+ #
24
+ # # bad
25
+ # {foo: bar}.collect { |k, v| [k.to_s, v.do_something] }.to_h
26
+ #
27
+ # # good
28
+ # {foo: bar}.to_h { |k, v| [k.to_s, v.do_something] }
29
+ #
30
+ class MapToHash < Base
31
+ extend AutoCorrector
32
+ extend TargetRubyVersion
33
+ include RangeHelp
34
+
35
+ minimum_target_ruby_version 2.6
36
+
37
+ MSG = 'Pass a block to `to_h` instead of calling `%<method>s.to_h`.'
38
+ RESTRICT_ON_SEND = %i[to_h].freeze
39
+
40
+ # @!method map_to_h?(node)
41
+ def_node_matcher :map_to_h?, <<~PATTERN
42
+ $(send (block $(send _ {:map :collect}) ...) :to_h)
43
+ PATTERN
44
+
45
+ def on_send(node)
46
+ return unless (to_h_node, map_node = map_to_h?(node))
47
+
48
+ message = format(MSG, method: map_node.loc.selector.source)
49
+ add_offense(map_node.loc.selector, message: message) do |corrector|
50
+ # If the `to_h` call already has a block, do not auto-correct.
51
+ next if to_h_node.block_node
52
+
53
+ autocorrect(corrector, to_h_node, map_node)
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def autocorrect(corrector, to_h, map)
60
+ removal_range = range_between(to_h.loc.dot.begin_pos, to_h.loc.selector.end_pos)
61
+
62
+ corrector.remove(range_with_surrounding_space(range: removal_range, side: :left))
63
+ corrector.replace(map.loc.selector, 'to_h')
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -13,9 +13,11 @@ module RuboCop
13
13
 
14
14
  private
15
15
 
16
+ # rubocop:disable Metrics/PerceivedComplexity
16
17
  def omit_parentheses(node)
17
18
  return unless node.parenthesized?
18
19
  return if inside_endless_method_def?(node)
20
+ return if require_parentheses_for_hash_value_omission?(node)
19
21
  return if syntax_like_method_call?(node)
20
22
  return if super_call_without_arguments?(node)
21
23
  return if allowed_camel_case_method_call?(node)
@@ -26,6 +28,7 @@ module RuboCop
26
28
  auto_correct(corrector, node)
27
29
  end
28
30
  end
31
+ # rubocop:enable Metrics/PerceivedComplexity
29
32
 
30
33
  def auto_correct(corrector, node)
31
34
  if parentheses_at_the_end_of_multiline_call?(node)
@@ -45,6 +48,23 @@ module RuboCop
45
48
  node.each_ancestor(:def, :defs).any?(&:endless?) && node.arguments.any?
46
49
  end
47
50
 
51
+ def require_parentheses_for_hash_value_omission?(node)
52
+ return false unless (last_argument = node.last_argument)
53
+ return false if !last_argument.hash_type? || !last_argument.pairs.last&.value_omission?
54
+
55
+ modifier_form?(node) || exist_next_line_expression?(node)
56
+ end
57
+
58
+ def modifier_form?(node)
59
+ node.parent.respond_to?(:modifier_form?) && node.parent.modifier_form?
60
+ end
61
+
62
+ # Require hash value omission be enclosed in parentheses to prevent the following issue:
63
+ # https://bugs.ruby-lang.org/issues/18396.
64
+ def exist_next_line_expression?(node)
65
+ node.parent&.assignment? ? node.parent.right_sibling : node.right_sibling
66
+ end
67
+
48
68
  def syntax_like_method_call?(node)
49
69
  node.implicit_call? || node.operator_method?
50
70
  end
@@ -43,9 +43,11 @@ module RuboCop
43
43
  # NOTE: Parentheses are still allowed in cases where omitting them
44
44
  # results in ambiguous or syntactically incorrect code. For example,
45
45
  # parentheses are required around a method with arguments when inside an
46
- # endless method definition introduced in Ruby 3.0. Parentheses are also
46
+ # endless method definition introduced in Ruby 3.0. Parentheses are also
47
47
  # allowed when forwarding arguments with the triple-dot syntax introduced
48
48
  # in Ruby 2.7 as omitting them starts an endless range.
49
+ # And Ruby 3.1's hash omission syntax has a case that requires parentheses
50
+ # because of the following issue: https://bugs.ruby-lang.org/issues/18396.
49
51
  #
50
52
  # @example EnforcedStyle: require_parentheses (default)
51
53
  #
@@ -6,8 +6,13 @@ module RuboCop
6
6
  # This cop checks for parentheses around the arguments in method
7
7
  # definitions. Both instance and class/singleton methods are checked.
8
8
  #
9
- # This cop does not consider endless methods, since parentheses are
10
- # always required for them.
9
+ # Regardless of style, parentheses are necessary for:
10
+ #
11
+ # 1. Endless methods
12
+ # 2. Argument lists containing a `forward-arg` (`...`)
13
+ # 3. Argument lists containing an anonymous block forwarding (`&`)
14
+ #
15
+ # Removing the parens would be a syntax error here.
11
16
  #
12
17
  # @example EnforcedStyle: require_parentheses (default)
13
18
  # # The `require_parentheses` style requires method definitions
@@ -121,21 +126,13 @@ module RuboCop
121
126
  corrector.remove(arg_node.loc.end)
122
127
  end
123
128
 
124
- def correct_definition(def_node, corrector)
125
- arguments_range = def_node.arguments.source_range
126
- args_with_space = range_with_surrounding_space(range: arguments_range, side: :left)
127
- leading_space = range_between(args_with_space.begin_pos, arguments_range.begin_pos)
128
- corrector.replace(leading_space, '(')
129
- corrector.insert_after(arguments_range, ')')
130
- end
131
-
132
129
  def forced_parentheses?(node)
133
130
  # Regardless of style, parentheses are necessary for:
134
131
  # 1. Endless methods
135
132
  # 2. Argument lists containing a `forward-arg` (`...`)
133
+ # 3. Argument lists containing an anonymous block forwarding (`&`)
136
134
  # Removing the parens would be a syntax error here.
137
-
138
- node.endless? || node.arguments.any?(&:forward_arg_type?)
135
+ node.endless? || node.arguments.any?(&:forward_arg_type?) || anonymous_block_arg?(node)
139
136
  end
140
137
 
141
138
  def require_parentheses?(args)
@@ -151,7 +148,8 @@ module RuboCop
151
148
  location = node.arguments.source_range
152
149
 
153
150
  add_offense(location, message: MSG_MISSING) do |corrector|
154
- correct_definition(node, corrector)
151
+ add_parentheses(node.arguments, corrector)
152
+
155
153
  unexpected_style_detected 'require_no_parentheses'
156
154
  end
157
155
  end
@@ -163,6 +161,12 @@ module RuboCop
163
161
  unexpected_style_detected 'require_parentheses'
164
162
  end
165
163
  end
164
+
165
+ def anonymous_block_arg?(node)
166
+ return false unless (last_argument = node.arguments.last)
167
+
168
+ last_argument.blockarg_type? && last_argument.name.nil?
169
+ end
166
170
  end
167
171
  end
168
172
  end
@@ -27,6 +27,11 @@ module RuboCop
27
27
  # # bad
28
28
  # 10_000_00 # typical representation of $10,000 in cents
29
29
  #
30
+ # @example AllowedNumbers: [3000]
31
+ #
32
+ # # good
33
+ # 3000 # You can specify allowed numbers. (e.g. port number)
34
+ #
30
35
  class NumericLiterals < Base
31
36
  include IntegerNode
32
37
  extend AutoCorrector
@@ -51,9 +56,9 @@ module RuboCop
51
56
 
52
57
  def check(node)
53
58
  int = integer_part(node)
54
-
55
59
  # TODO: handle non-decimal literals as well
56
60
  return if int.start_with?('0')
61
+ return if allowed_numbers.include?(int)
57
62
  return unless int.size >= min_digits
58
63
 
59
64
  case int
@@ -99,6 +104,10 @@ module RuboCop
99
104
  def min_digits
100
105
  cop_config['MinDigits']
101
106
  end
107
+
108
+ def allowed_numbers
109
+ cop_config.fetch('AllowedNumbers', []).map(&:to_s)
110
+ end
102
111
  end
103
112
  end
104
113
  end
@@ -45,7 +45,7 @@ module RuboCop
45
45
 
46
46
  message = message(node)
47
47
  add_offense(node, message: message) do |corrector|
48
- corrector.replace(node, replacement(node))
48
+ autocorrect(corrector, node)
49
49
  end
50
50
  end
51
51
 
@@ -55,55 +55,30 @@ module RuboCop
55
55
  format(MSG, keyword: node.keyword)
56
56
  end
57
57
 
58
- def replacement(node)
58
+ def autocorrect(corrector, node)
59
59
  if always_multiline? || cannot_replace_to_ternary?(node)
60
- multiline_replacement(node)
60
+ IfThenCorrector.new(node, indentation: indentation_width).call(corrector)
61
61
  else
62
- replaced_node = ternary_replacement(node)
63
- return replaced_node unless node.parent
64
- return "(#{replaced_node})" if %i[and or].include?(node.parent.type)
65
- return "(#{replaced_node})" if node.parent.send_type? && node.parent.operator_method?
66
-
67
- replaced_node
62
+ corrector.replace(node, ternary_correction(node))
68
63
  end
69
64
  end
70
65
 
71
- def always_multiline?
72
- @config.for_cop('Style/OneLineConditional')['AlwaysCorrectToMultiline']
73
- end
66
+ def ternary_correction(node)
67
+ replaced_node = ternary_replacement(node)
74
68
 
75
- def cannot_replace_to_ternary?(node)
76
- node.elsif_conditional?
77
- end
69
+ return replaced_node unless node.parent
70
+ return "(#{replaced_node})" if %i[and or].include?(node.parent.type)
71
+ return "(#{replaced_node})" if node.parent.send_type? && node.parent.operator_method?
78
72
 
79
- def multiline_replacement(node, indentation = nil)
80
- indentation = ' ' * node.source_range.column if indentation.nil?
81
- if_branch_source = node.if_branch&.source || 'nil'
82
- elsif_indentation = indentation if node.respond_to?(:elsif?) && node.elsif?
83
- if_branch = <<~RUBY
84
- #{elsif_indentation}#{node.keyword} #{node.condition.source}
85
- #{indentation}#{branch_body_indentation}#{if_branch_source}
86
- RUBY
87
- else_branch = else_branch_to_multiline(node.else_branch, indentation)
88
- if_branch + else_branch
73
+ replaced_node
89
74
  end
90
75
 
91
- def else_branch_to_multiline(else_branch, indentation)
92
- if else_branch.nil?
93
- 'end'
94
- elsif else_branch.if_type? && else_branch.elsif?
95
- multiline_replacement(else_branch, indentation)
96
- else
97
- <<~RUBY.chomp
98
- #{indentation}else
99
- #{indentation}#{branch_body_indentation}#{else_branch.source}
100
- #{indentation}end
101
- RUBY
102
- end
76
+ def always_multiline?
77
+ @config.for_cop('Style/OneLineConditional')['AlwaysCorrectToMultiline']
103
78
  end
104
79
 
105
- def branch_body_indentation
106
- ' ' * (@config.for_cop('Layout/IndentationWidth')['Width'] || 2)
80
+ def cannot_replace_to_ternary?(node)
81
+ node.elsif_conditional?
107
82
  end
108
83
 
109
84
  def ternary_replacement(node)
@@ -141,6 +116,10 @@ module RuboCop
141
116
 
142
117
  node.respond_to?(:arguments?) && node.arguments? && !node.parenthesized_call?
143
118
  end
119
+
120
+ def indentation_width
121
+ @config.for_cop('Layout/IndentationWidth')['Width']
122
+ end
144
123
  end
145
124
  end
146
125
  end
@@ -97,7 +97,7 @@ module RuboCop
97
97
  offense_range = node.loc.begin
98
98
 
99
99
  add_offense(offense_range) do |corrector|
100
- if any_ancestor_assignment_node?(node)
100
+ if node.parent&.assignment?
101
101
  replace_begin_with_statement(corrector, offense_range, node)
102
102
  else
103
103
  corrector.remove(offense_range)
@@ -170,11 +170,7 @@ module RuboCop
170
170
  end
171
171
 
172
172
  def valid_begin_assignment?(node)
173
- any_ancestor_assignment_node?(node) && !node.children.one?
174
- end
175
-
176
- def any_ancestor_assignment_node?(node)
177
- node.each_ancestor.any?(&:assignment?)
173
+ node.parent&.assignment? && !node.children.one?
178
174
  end
179
175
  end
180
176
  end
@@ -82,10 +82,20 @@ module RuboCop
82
82
  end
83
83
 
84
84
  def autocorrect_single_variable_interpolation(corrector, embedded_node, node)
85
- variable_loc = embedded_node.children.first.loc
86
- replacement = "#{variable_loc.expression.source}.to_s"
85
+ embedded_var = embedded_node.children.first
87
86
 
88
- corrector.replace(node, replacement)
87
+ source = if require_parentheses?(embedded_var)
88
+ receiver = range_between(
89
+ embedded_var.loc.expression.begin_pos, embedded_var.loc.selector.end_pos
90
+ )
91
+ arguments = embedded_var.arguments.map(&:source).join(', ')
92
+
93
+ "#{receiver.source}(#{arguments})"
94
+ else
95
+ embedded_var.source
96
+ end
97
+
98
+ corrector.replace(node, "#{source}.to_s")
89
99
  end
90
100
 
91
101
  def autocorrect_other(corrector, embedded_node, node)
@@ -97,6 +107,10 @@ module RuboCop
97
107
  corrector.replace(embedded_loc.begin, '(')
98
108
  corrector.replace(embedded_loc.end, ').to_s')
99
109
  end
110
+
111
+ def require_parentheses?(node)
112
+ node.send_type? && !node.arguments.count.zero? && !node.parenthesized_call?
113
+ end
100
114
  end
101
115
  end
102
116
  end
@@ -80,7 +80,11 @@ module RuboCop
80
80
  end
81
81
 
82
82
  def without_character_class(loc)
83
- loc.source[1..-2]
83
+ without_character_class = loc.source[1..-2]
84
+
85
+ # Adds `\` to prevent auto-correction that changes to an interpolated string when `[#]`.
86
+ # e.g. From `/[#]{0}/` to `/#{0}/`
87
+ loc.source == '[#]' ? "\\#{without_character_class}" : without_character_class
84
88
  end
85
89
 
86
90
  def whitespace_in_free_space_mode?(node, elem)
@@ -53,7 +53,7 @@ module RuboCop
53
53
  yield __FILE__ __LINE__ __ENCODING__].freeze
54
54
 
55
55
  def self.autocorrect_incompatible_with
56
- [ColonMethodCall]
56
+ [ColonMethodCall, Layout::DotPosition]
57
57
  end
58
58
 
59
59
  def initialize(config = nil, options = nil)
@@ -218,11 +218,7 @@ module RuboCop
218
218
  def find_matching_receiver_invocation(method_chain, checked_variable)
219
219
  return nil unless method_chain
220
220
 
221
- receiver = if method_chain.block_type?
222
- method_chain.send_node.receiver
223
- else
224
- method_chain.receiver
225
- end
221
+ receiver = method_chain.receiver
226
222
 
227
223
  return receiver if receiver == checked_variable
228
224
 
@@ -91,11 +91,12 @@ module RuboCop
91
91
  second.int_type? ? second.to_a.first : :unknown
92
92
  end
93
93
 
94
- def range_size(range_node) # rubocop:todo Metrics/CyclomaticComplexity
94
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
95
+ def range_size(range_node)
95
96
  vals = range_node.to_a
96
- return :unknown unless vals.all?(&:int_type?)
97
+ return :unknown unless vals.all? { |val| val.nil? || val.int_type? }
97
98
 
98
- low, high = vals.map { |val| val.children[0] }
99
+ low, high = vals.map { |val| val.nil? ? 0 : val.children[0] }
99
100
  return :unknown unless low.zero? && high >= 0
100
101
 
101
102
  case range_node.type
@@ -105,6 +106,7 @@ module RuboCop
105
106
  (low..high).size
106
107
  end
107
108
  end
109
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
108
110
 
109
111
  def source_range(shuffle_node, node)
110
112
  Parser::Source::Range.new(shuffle_node.source_range.source_buffer,
@@ -39,7 +39,7 @@ module RuboCop
39
39
  return unless eligible_method?(node)
40
40
  return unless eligible_arguments?(node)
41
41
 
42
- method_name = node.send_node.method_name
42
+ method_name = node.method_name
43
43
  return if args_match?(method_name, node.arguments)
44
44
 
45
45
  preferred_block_arguments = build_preferred_arguments_map(node, target_args(method_name))
@@ -81,7 +81,7 @@ module RuboCop
81
81
  end
82
82
 
83
83
  def eligible_method?(node)
84
- node.send_node.receiver && method_names.include?(node.send_node.method_name)
84
+ node.receiver && method_names.include?(node.method_name)
85
85
  end
86
86
 
87
87
  def methods
@@ -82,7 +82,9 @@ module RuboCop
82
82
  end
83
83
 
84
84
  def autocorrect(corrector, node, if_branch)
85
- corrector.wrap(node.condition, '(', ')') if node.condition.or_type?
85
+ if node.condition.or_type? || node.condition.assignment?
86
+ corrector.wrap(node.condition, '(', ')')
87
+ end
86
88
 
87
89
  correct_from_unless_to_if(corrector, node) if node.unless?
88
90
 
@@ -58,6 +58,8 @@ module RuboCop
58
58
  end
59
59
 
60
60
  def simple_assignment?(node)
61
+ return false unless node.respond_to?(:type)
62
+
61
63
  SIMPLE_ASSIGNMENT_TYPES.include?(node.type)
62
64
  end
63
65
 
@@ -67,7 +67,10 @@ module RuboCop
67
67
  MSG_COMPLEX = '%<command>s parentheses for ternary expressions with complex conditions.'
68
68
 
69
69
  def on_if(node)
70
- return if only_closing_parenthesis_is_last_line?(node.condition)
70
+ condition = node.condition
71
+
72
+ return if only_closing_parenthesis_is_last_line?(condition)
73
+ return if condition_as_parenthesized_one_line_pattern_matching?(condition)
71
74
  return unless node.ternary? && !infinite_loop? && offense?(node)
72
75
 
73
76
  message = message(node)
@@ -77,11 +80,22 @@ module RuboCop
77
80
  end
78
81
  end
79
82
 
83
+ private
84
+
80
85
  def only_closing_parenthesis_is_last_line?(condition)
81
86
  condition.source.split("\n").last == ')'
82
87
  end
83
88
 
84
- private
89
+ def condition_as_parenthesized_one_line_pattern_matching?(condition)
90
+ return false unless condition.parenthesized_call?
91
+ return false unless (first_child = condition.children.first)
92
+
93
+ if target_ruby_version >= 3.0
94
+ first_child.match_pattern_p_type?
95
+ else
96
+ first_child.match_pattern_type? # For Ruby 2.7's one line pattern matching AST.
97
+ end
98
+ end
85
99
 
86
100
  def autocorrect(corrector, node)
87
101
  condition = node.condition
@@ -131,7 +131,7 @@ module RuboCop
131
131
  @options[:stdin] = new_source
132
132
  else
133
133
  filename = processed_source.buffer.name
134
- File.open(filename, 'w') { |f| f.write(new_source) }
134
+ File.write(filename, new_source)
135
135
  end
136
136
  @updated_source_file = true
137
137
  end
@@ -30,8 +30,15 @@ module RuboCop
30
30
  node.loc.respond_to?(:end) && node.loc.end && node.loc.end.is?(')')
31
31
  end
32
32
 
33
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
33
34
  def add_parentheses(node, corrector)
34
- if !node.respond_to?(:arguments)
35
+ if node.args_type?
36
+ arguments_range = node.source_range
37
+ args_with_space = range_with_surrounding_space(range: arguments_range, side: :left)
38
+ leading_space = range_between(args_with_space.begin_pos, arguments_range.begin_pos)
39
+ corrector.replace(leading_space, '(')
40
+ corrector.insert_after(arguments_range, ')')
41
+ elsif !node.respond_to?(:arguments)
35
42
  corrector.wrap(node, '(', ')')
36
43
  elsif node.arguments.empty?
37
44
  corrector.insert_after(node, '()')
@@ -43,6 +50,7 @@ module RuboCop
43
50
  corrector.insert_after(args_end(node), ')')
44
51
  end
45
52
  end
53
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
46
54
 
47
55
  def args_begin(node)
48
56
  loc = node.loc
@@ -121,9 +121,14 @@ module RuboCop
121
121
  output_buffer.puts "# Offense count: #{offense_count}" if show_offense_counts?
122
122
 
123
123
  cop_class = Cop::Registry.global.find_by_cop_name(cop_name)
124
- output_buffer.puts '# Cop supports --auto-correct.' if cop_class&.support_autocorrect?
125
-
126
124
  default_cfg = default_config(cop_name)
125
+
126
+ if supports_safe_auto_correct?(cop_class, default_cfg)
127
+ output_buffer.puts '# Cop supports --auto-correct.'
128
+ elsif supports_unsafe_autocorrect?(cop_class, default_cfg)
129
+ output_buffer.puts '# Cop supports --auto-correct-all.'
130
+ end
131
+
127
132
  return unless default_cfg
128
133
 
129
134
  params = cop_config_params(default_cfg, cfg)
@@ -132,6 +137,15 @@ module RuboCop
132
137
  output_cop_param_comments(output_buffer, params, default_cfg)
133
138
  end
134
139
 
140
+ def supports_safe_auto_correct?(cop_class, default_cfg)
141
+ cop_class&.support_autocorrect? &&
142
+ (default_cfg.nil? || default_cfg['Safe'] || default_cfg['Safe'].nil?)
143
+ end
144
+
145
+ def supports_unsafe_autocorrect?(cop_class, default_cfg)
146
+ cop_class&.support_autocorrect? && !default_cfg.nil? && default_cfg['Safe'] == false
147
+ end
148
+
135
149
  def cop_config_params(default_cfg, cfg)
136
150
  default_cfg.keys -
137
151
  %w[Description StyleGuide Reference Enabled Exclude Safe
@@ -15,7 +15,7 @@ module RuboCop
15
15
  'root of the project. RuboCop will use this path to determine which ' \
16
16
  'cops are enabled (via eg. Include/Exclude), and so that certain cops ' \
17
17
  'like Naming/FileName can be checked.'
18
- EXITING_OPTIONS = %i[version verbose_version show_cops].freeze
18
+ EXITING_OPTIONS = %i[version verbose_version show_cops show_docs_url].freeze
19
19
  DEFAULT_MAXIMUM_EXCLUSION_ITEMS = 15
20
20
 
21
21
  def initialize
@@ -188,6 +188,9 @@ module RuboCop
188
188
  option(opts, '--show-cops [COP1,COP2,...]') do |list|
189
189
  @options[:show_cops] = list.nil? ? [] : list.split(',')
190
190
  end
191
+ option(opts, '--show-docs-url [COP1,COP2,...]') do |list|
192
+ @options[:show_docs_url] = list.nil? ? [] : list.split(',')
193
+ end
191
194
  end
192
195
  end
193
196
 
@@ -475,6 +478,8 @@ module RuboCop
475
478
  show_cops: ['Shows the given cops, or all cops by',
476
479
  'default, and their configurations for the',
477
480
  'current directory.'],
481
+ show_docs_url: ['Display url to documentation for the given',
482
+ 'cops, or base url by default.'],
478
483
  fail_fast: ['Inspect files in order of modification',
479
484
  'time and stop after the first file',
480
485
  'containing offenses.'],
@@ -23,9 +23,7 @@ module RuboCop
23
23
  next if response.is_a?(Net::HTTPNotModified)
24
24
  next if response.is_a?(SocketError)
25
25
 
26
- File.open cache_path, 'w' do |io|
27
- io.write response.body
28
- end
26
+ File.write(cache_path, response.body)
29
27
  end
30
28
 
31
29
  cache_path
@@ -179,7 +179,7 @@ module RuboCop
179
179
  .select { |path| File.file?(path) }
180
180
  .sort!
181
181
  .each do |path|
182
- content = File.open(path, 'rb', &:read)
182
+ content = File.binread(path)
183
183
  digest << Zlib.crc32(content).to_s # mtime not reliable
184
184
  end
185
185
  digest << RuboCop::Version::STRING << RuboCop::AST::Version::STRING
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '1.23.0'
6
+ STRING = '1.25.1'
7
7
 
8
8
  MSG = '%<version>s (using Parser %<parser_version>s, '\
9
9
  'rubocop-ast %<rubocop_ast_version>s, ' \