rubocop 1.23.0 → 1.25.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 (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, ' \