rubocop 1.30.1 → 1.31.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 (125) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +41 -8
  4. data/config/obsoletion.yml +2 -0
  5. data/exe/rubocop +15 -7
  6. data/lib/rubocop/cli/command/auto_genenerate_config.rb +1 -1
  7. data/lib/rubocop/cli/command/suggest_extensions.rb +3 -3
  8. data/lib/rubocop/config.rb +4 -0
  9. data/lib/rubocop/config_loader.rb +1 -0
  10. data/lib/rubocop/config_validator.rb +3 -3
  11. data/lib/rubocop/cop/base.rb +4 -0
  12. data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -1
  13. data/lib/rubocop/cop/bundler/gem_filename.rb +4 -4
  14. data/lib/rubocop/cop/bundler/ordered_gems.rb +2 -2
  15. data/lib/rubocop/cop/corrector.rb +2 -2
  16. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +3 -3
  17. data/lib/rubocop/cop/correctors/unused_arg_corrector.rb +1 -1
  18. data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +31 -16
  19. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +1 -1
  20. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
  21. data/lib/rubocop/cop/gemspec/require_mfa.rb +20 -20
  22. data/lib/rubocop/cop/generator.rb +1 -1
  23. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -5
  24. data/lib/rubocop/cop/internal_affairs/redundant_location_argument.rb +1 -1
  25. data/lib/rubocop/cop/internal_affairs/redundant_message_argument.rb +1 -1
  26. data/lib/rubocop/cop/layout/empty_comment.rb +1 -1
  27. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +25 -4
  28. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +17 -11
  29. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +49 -10
  30. data/lib/rubocop/cop/layout/initial_indentation.rb +1 -1
  31. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +68 -0
  32. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +130 -0
  33. data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -1
  34. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +2 -2
  35. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -2
  36. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +1 -1
  37. data/lib/rubocop/cop/layout/space_around_operators.rb +1 -1
  38. data/lib/rubocop/cop/layout/space_before_block_braces.rb +1 -1
  39. data/lib/rubocop/cop/layout/space_before_first_arg.rb +1 -1
  40. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +5 -3
  41. data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -1
  42. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +51 -0
  43. data/lib/rubocop/cop/lint/interpolation_check.rb +1 -1
  44. data/lib/rubocop/cop/lint/literal_as_condition.rb +5 -0
  45. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +122 -0
  46. data/lib/rubocop/cop/lint/number_conversion.rb +3 -3
  47. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -1
  48. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +5 -5
  49. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +1 -1
  50. data/lib/rubocop/cop/lint/redundant_require_statement.rb +1 -1
  51. data/lib/rubocop/cop/lint/regexp_as_condition.rb +2 -2
  52. data/lib/rubocop/cop/lint/struct_new_override.rb +2 -2
  53. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -1
  54. data/lib/rubocop/cop/lint/useless_else_without_rescue.rb +44 -0
  55. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  56. data/lib/rubocop/cop/mixin/multiline_element_indentation.rb +41 -12
  57. data/lib/rubocop/cop/mixin/range_help.rb +7 -3
  58. data/lib/rubocop/cop/style/accessor_grouping.rb +1 -1
  59. data/lib/rubocop/cop/style/arguments_forwarding.rb +1 -1
  60. data/lib/rubocop/cop/style/block_delimiters.rb +4 -2
  61. data/lib/rubocop/cop/style/commented_keyword.rb +1 -1
  62. data/lib/rubocop/cop/style/conditional_assignment.rb +1 -0
  63. data/lib/rubocop/cop/style/empty_method.rb +16 -1
  64. data/lib/rubocop/cop/style/encoding.rb +1 -1
  65. data/lib/rubocop/cop/style/explicit_block_argument.rb +1 -1
  66. data/lib/rubocop/cop/style/format_string_token.rb +48 -17
  67. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +1 -1
  68. data/lib/rubocop/cop/style/guard_clause.rb +8 -6
  69. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +1 -1
  70. data/lib/rubocop/cop/style/hash_except.rb +88 -8
  71. data/lib/rubocop/cop/style/hash_syntax.rb +2 -2
  72. data/lib/rubocop/cop/style/implicit_runtime_error.rb +2 -2
  73. data/lib/rubocop/cop/style/keyword_parameters_order.rb +1 -1
  74. data/lib/rubocop/cop/style/line_end_concatenation.rb +1 -1
  75. data/lib/rubocop/cop/style/map_to_hash.rb +1 -1
  76. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -2
  77. data/lib/rubocop/cop/style/multiline_if_modifier.rb +2 -2
  78. data/lib/rubocop/cop/style/multiline_if_then.rb +1 -1
  79. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +1 -3
  80. data/lib/rubocop/cop/style/multiline_method_signature.rb +1 -1
  81. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +1 -1
  82. data/lib/rubocop/cop/style/multiline_when_then.rb +1 -3
  83. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +1 -1
  84. data/lib/rubocop/cop/style/nested_ternary_operator.rb +19 -7
  85. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  86. data/lib/rubocop/cop/style/not.rb +1 -1
  87. data/lib/rubocop/cop/style/redundant_argument.rb +1 -1
  88. data/lib/rubocop/cop/style/redundant_return.rb +1 -1
  89. data/lib/rubocop/cop/style/redundant_self_assignment.rb +1 -1
  90. data/lib/rubocop/cop/style/sole_nested_conditional.rb +2 -2
  91. data/lib/rubocop/cop/style/struct_inheritance.rb +2 -2
  92. data/lib/rubocop/cop/style/swap_values.rb +1 -1
  93. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  94. data/lib/rubocop/cop/style/unpack_first.rb +1 -1
  95. data/lib/rubocop/cop/util.rb +1 -1
  96. data/lib/rubocop/formatter/formatter_set.rb +20 -19
  97. data/lib/rubocop/formatter/git_hub_actions_formatter.rb +15 -2
  98. data/lib/rubocop/formatter/html_formatter.rb +0 -1
  99. data/lib/rubocop/formatter/offense_count_formatter.rb +2 -0
  100. data/lib/rubocop/formatter/simple_text_formatter.rb +6 -7
  101. data/lib/rubocop/formatter.rb +31 -0
  102. data/lib/rubocop/options.rb +24 -1
  103. data/lib/rubocop/rake_task.rb +30 -9
  104. data/lib/rubocop/server/cache.rb +109 -0
  105. data/lib/rubocop/server/cli.rb +104 -0
  106. data/lib/rubocop/server/client_command/base.rb +44 -0
  107. data/lib/rubocop/server/client_command/exec.rb +59 -0
  108. data/lib/rubocop/server/client_command/restart.rb +25 -0
  109. data/lib/rubocop/server/client_command/start.rb +43 -0
  110. data/lib/rubocop/server/client_command/status.rb +28 -0
  111. data/lib/rubocop/server/client_command/stop.rb +31 -0
  112. data/lib/rubocop/server/client_command.rb +26 -0
  113. data/lib/rubocop/server/core.rb +79 -0
  114. data/lib/rubocop/server/errors.rb +23 -0
  115. data/lib/rubocop/server/helper.rb +34 -0
  116. data/lib/rubocop/server/server_command/base.rb +50 -0
  117. data/lib/rubocop/server/server_command/exec.rb +34 -0
  118. data/lib/rubocop/server/server_command/stop.rb +24 -0
  119. data/lib/rubocop/server/server_command.rb +21 -0
  120. data/lib/rubocop/server/socket_reader.rb +65 -0
  121. data/lib/rubocop/server.rb +53 -0
  122. data/lib/rubocop/version.rb +15 -8
  123. data/lib/rubocop.rb +7 -26
  124. metadata +42 -5
  125. data/lib/rubocop/cop/gemspec/date_assignment.rb +0 -49
@@ -71,16 +71,16 @@ module RuboCop
71
71
  processed_source.comment_config.comment_only_line?(directive_comment_range.line) &&
72
72
  directive_comment_range.begin_pos == line_comment_range.begin_pos
73
73
  # When the previous line is blank, it should be retained
74
- range_with_surrounding_space(range: directive_comment_range, side: :right)
74
+ range_with_surrounding_space(directive_comment_range, side: :right)
75
75
  else
76
76
  # Eat the entire comment, the preceding space, and the preceding
77
77
  # newline if there is one.
78
78
  original_begin = directive_comment_range.begin_pos
79
79
  range = range_with_surrounding_space(
80
- range: directive_comment_range, side: :left, newlines: true
80
+ directive_comment_range, side: :left, newlines: true
81
81
  )
82
82
 
83
- range_with_surrounding_space(range: range,
83
+ range_with_surrounding_space(range,
84
84
  side: :right,
85
85
  # Special for a comment that
86
86
  # begins the file: remove
@@ -94,13 +94,13 @@ module RuboCop
94
94
  # is NOT being removed?
95
95
  if ends_its_line?(ranges.last) && trailing_range?(ranges, range)
96
96
  # Eat the comma on the left.
97
- range = range_with_surrounding_space(range: range, side: :left)
97
+ range = range_with_surrounding_space(range, side: :left)
98
98
  range = range_with_surrounding_comma(range, :left)
99
99
  end
100
100
 
101
101
  range = range_with_surrounding_comma(range, :right)
102
102
  # Eat following spaces up to EOL, but not the newline itself.
103
- range_with_surrounding_space(range: range, side: :right, newlines: false)
103
+ range_with_surrounding_space(range, side: :right, newlines: false)
104
104
  end
105
105
 
106
106
  def each_redundant_disable(&block)
@@ -60,7 +60,7 @@ module RuboCop
60
60
  message: format(MSG, cop: all_or_name(name))
61
61
  ) do |corrector|
62
62
  if directive.match?(cop_names)
63
- corrector.remove(range_with_surrounding_space(range: directive.range, side: :right))
63
+ corrector.remove(range_with_surrounding_space(directive.range, side: :right))
64
64
  else
65
65
  corrector.remove(range_with_comma(comment, name))
66
66
  end
@@ -41,7 +41,7 @@ module RuboCop
41
41
  return unless unnecessary_require_statement?(node)
42
42
 
43
43
  add_offense(node) do |corrector|
44
- range = range_with_surrounding_space(range: node.loc.expression, side: :right)
44
+ range = range_with_surrounding_space(node.loc.expression, side: :right)
45
45
 
46
46
  corrector.remove(range)
47
47
  end
@@ -19,8 +19,8 @@ module RuboCop
19
19
  class RegexpAsCondition < Base
20
20
  extend AutoCorrector
21
21
 
22
- MSG = 'Do not use regexp literal as a condition.' \
23
- ' The regexp literal matches `$_` implicitly.'
22
+ MSG = 'Do not use regexp literal as a condition. ' \
23
+ 'The regexp literal matches `$_` implicitly.'
24
24
 
25
25
  def on_match_current_line(node)
26
26
  add_offense(node) { |corrector| corrector.replace(node, "#{node.source} =~ $_") }
@@ -22,8 +22,8 @@ module RuboCop
22
22
  # g.count #=> 2
23
23
  #
24
24
  class StructNewOverride < Base
25
- MSG = '`%<member_name>s` member overrides `Struct#%<method_name>s`' \
26
- ' and it may be unexpected.'
25
+ MSG = '`%<member_name>s` member overrides `Struct#%<method_name>s` ' \
26
+ 'and it may be unexpected.'
27
27
  RESTRICT_ON_SEND = %i[new].freeze
28
28
 
29
29
  STRUCT_METHOD_NAMES = Struct.instance_methods
@@ -45,7 +45,7 @@ module RuboCop
45
45
 
46
46
  def trailing_comma_range(node)
47
47
  range_with_surrounding_space(
48
- range: node.arguments[-2].source_range,
48
+ node.arguments[-2].source_range,
49
49
  side: :right
50
50
  ).end.resize(1)
51
51
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # Checks for useless `else` in `begin..end` without `rescue`.
7
+ #
8
+ # NOTE: This syntax is no longer valid on Ruby 2.6 or higher.
9
+ #
10
+ # @example
11
+ #
12
+ # # bad
13
+ #
14
+ # begin
15
+ # do_something
16
+ # else
17
+ # do_something_else # This will never be run.
18
+ # end
19
+ #
20
+ # @example
21
+ #
22
+ # # good
23
+ #
24
+ # begin
25
+ # do_something
26
+ # rescue
27
+ # handle_errors
28
+ # else
29
+ # do_something_else
30
+ # end
31
+ class UselessElseWithoutRescue < Base
32
+ MSG = '`else` without `rescue` is useless.'
33
+
34
+ def on_new_investigation
35
+ processed_source.diagnostics.each do |diagnostic|
36
+ next unless diagnostic.reason == :useless_else
37
+
38
+ add_offense(diagnostic.location, severity: diagnostic.level)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -49,7 +49,7 @@ module RuboCop
49
49
  when :heredoc
50
50
  ->(node) { heredoc_node?(node) }
51
51
  else
52
- raise ArgumentError, "Unknown foldable type: #{type.inspect}. "\
52
+ raise ArgumentError, "Unknown foldable type: #{type.inspect}. " \
53
53
  "Valid foldable types are: #{FOLDABLE_TYPES.join(', ')}."
54
54
  end
55
55
  end
@@ -25,15 +25,17 @@ module RuboCop
25
25
 
26
26
  def check_first(first, left_brace, left_parenthesis, offset)
27
27
  actual_column = first.source_range.column
28
- expected_column = base_column(left_brace, left_parenthesis) +
29
- configured_indentation_width + offset
28
+
29
+ indent_base_column, indent_base_type = indent_base(left_brace, left_parenthesis)
30
+ expected_column = indent_base_column + configured_indentation_width + offset
31
+
30
32
  @column_delta = expected_column - actual_column
31
33
  styles = detected_styles(actual_column, offset, left_parenthesis, left_brace)
32
34
 
33
35
  if @column_delta.zero?
34
36
  check_expected_style(styles)
35
37
  else
36
- incorrect_style_detected(styles, first, left_parenthesis)
38
+ incorrect_style_detected(styles, first, indent_base_type)
37
39
  end
38
40
  end
39
41
 
@@ -45,14 +47,41 @@ module RuboCop
45
47
  end
46
48
  end
47
49
 
48
- def base_column(left_brace, left_parenthesis)
49
- if style == brace_alignment_style
50
- left_brace.column
51
- elsif left_parenthesis && style == :special_inside_parentheses
52
- left_parenthesis.column + 1
53
- else
54
- left_brace.source_line =~ /\S/
50
+ def indent_base(left_brace, left_parenthesis)
51
+ return [left_brace.column, :left_brace_or_bracket] if style == brace_alignment_style
52
+
53
+ pair = hash_pair_where_value_beginning_with(left_brace)
54
+ if pair && key_and_value_begin_on_same_line?(pair) &&
55
+ right_sibling_begins_on_subsequent_line?(pair)
56
+ return [pair.loc.column, :parent_hash_key]
55
57
  end
58
+
59
+ if left_parenthesis && style == :special_inside_parentheses
60
+ return [left_parenthesis.column + 1, :first_colmn_after_left_parenthesis]
61
+ end
62
+
63
+ [left_brace.source_line =~ /\S/, :start_of_line]
64
+ end
65
+
66
+ def hash_pair_where_value_beginning_with(left_brace)
67
+ node = node_beginning_with(left_brace)
68
+ node.parent&.pair_type? ? node.parent : nil
69
+ end
70
+
71
+ def node_beginning_with(left_brace)
72
+ processed_source.ast.each_descendant do |node|
73
+ if node.loc.is_a?(Parser::Source::Map::Collection) && (node.loc.begin == left_brace)
74
+ break node
75
+ end
76
+ end
77
+ end
78
+
79
+ def key_and_value_begin_on_same_line?(pair)
80
+ same_line?(pair.key, pair.value)
81
+ end
82
+
83
+ def right_sibling_begins_on_subsequent_line?(pair)
84
+ pair.right_sibling && (pair.last_line < pair.right_sibling.first_line)
56
85
  end
57
86
 
58
87
  def detected_styles(actual_column, offset, left_parenthesis, left_brace)
@@ -73,8 +102,8 @@ module RuboCop
73
102
  styles
74
103
  end
75
104
 
76
- def incorrect_style_detected(styles, first, left_parenthesis)
77
- msg = message(base_description(left_parenthesis))
105
+ def incorrect_style_detected(styles, first, base_column_type)
106
+ msg = message(base_description(base_column_type))
78
107
 
79
108
  add_offense(first, message: msg) do |corrector|
80
109
  autocorrect(corrector, first)
@@ -51,9 +51,13 @@ module RuboCop
51
51
  Parser::Source::Range.new(buffer, begin_pos, end_pos)
52
52
  end
53
53
 
54
- def range_with_surrounding_space(range:, side: :both,
55
- newlines: true, whitespace: false,
56
- continuations: false)
54
+ NOT_GIVEN = Module.new
55
+ def range_with_surrounding_space(range_positional = NOT_GIVEN, # rubocop:disable Metrics/ParameterLists
56
+ range: NOT_GIVEN, side: :both, newlines: true,
57
+ whitespace: false, continuations: false)
58
+
59
+ range = range_positional unless range_positional == NOT_GIVEN
60
+
57
61
  buffer = @processed_source.buffer
58
62
  src = buffer.source
59
63
 
@@ -72,7 +72,7 @@ module RuboCop
72
72
  if (preferred_accessors = preferred_accessors(node))
73
73
  corrector.replace(node, preferred_accessors)
74
74
  else
75
- range = range_with_surrounding_space(range: node.loc.expression, side: :left)
75
+ range = range_with_surrounding_space(node.loc.expression, side: :left)
76
76
  corrector.remove(range)
77
77
  end
78
78
  end
@@ -133,7 +133,7 @@ module RuboCop
133
133
  def register_offense_to_method_definition_arguments(method_definition)
134
134
  add_offense(arguments_range(method_definition)) do |corrector|
135
135
  arguments_range = range_with_surrounding_space(
136
- range: method_definition.arguments.source_range, side: :left
136
+ method_definition.arguments.source_range, side: :left
137
137
  )
138
138
  corrector.replace(arguments_range, '(...)')
139
139
  end
@@ -184,6 +184,8 @@ module RuboCop
184
184
  end
185
185
  end
186
186
 
187
+ alias on_numblock on_block
188
+
187
189
  private
188
190
 
189
191
  def autocorrect(corrector, node)
@@ -281,7 +283,7 @@ module RuboCop
281
283
  def move_comment_before_block(corrector, comment, block_node, closing_brace)
282
284
  range = block_node.chained? ? end_of_chain(block_node.parent).source_range : closing_brace
283
285
  comment_range = range_between(range.end_pos, comment.loc.expression.end_pos)
284
- corrector.remove(range_with_surrounding_space(range: comment_range, side: :right))
286
+ corrector.remove(range_with_surrounding_space(comment_range, side: :right))
285
287
  corrector.insert_after(range, "\n")
286
288
 
287
289
  corrector.insert_before(block_node, "#{comment.text}\n")
@@ -300,7 +302,7 @@ module RuboCop
300
302
 
301
303
  def get_blocks(node, &block)
302
304
  case node.type
303
- when :block
305
+ when :block, :numblock
304
306
  yield node
305
307
  when :send
306
308
  get_blocks(node.receiver, &block) if node.receiver
@@ -66,7 +66,7 @@ module RuboCop
66
66
 
67
67
  def register_offense(comment, matched_keyword)
68
68
  add_offense(comment, message: format(MSG, keyword: matched_keyword)) do |corrector|
69
- range = range_with_surrounding_space(range: comment.loc.expression, newlines: false)
69
+ range = range_with_surrounding_space(comment.loc.expression, newlines: false)
70
70
  corrector.remove(range)
71
71
 
72
72
  unless matched_keyword == 'end'
@@ -237,6 +237,7 @@ module RuboCop
237
237
  ASSIGNMENT_TYPES.each do |type|
238
238
  define_method "on_#{type}" do |node|
239
239
  return if part_of_ignored_node?(node)
240
+ return if node.parent&.shorthand_asgn?
240
241
 
241
242
  check_assignment_to_condition(node)
242
243
  end
@@ -11,6 +11,10 @@ module RuboCop
11
11
  # NOTE: A method definition is not considered empty if it contains
12
12
  # comments.
13
13
  #
14
+ # NOTE: Autocorrection will not be applied for the `compact` style
15
+ # if the resulting code is longer than the `Max` configuration for
16
+ # `Layout/LineLength`, but an offense will still be registered.
17
+ #
14
18
  # @example EnforcedStyle: compact (default)
15
19
  # # bad
16
20
  # def foo(bar)
@@ -51,7 +55,12 @@ module RuboCop
51
55
  return if node.body || comment_lines?(node)
52
56
  return if correct_style?(node)
53
57
 
54
- add_offense(node) { |corrector| corrector.replace(node, corrected(node)) }
58
+ add_offense(node) do |corrector|
59
+ correction = corrected(node)
60
+ next if compact_style? && max_line_length && correction.size > max_line_length
61
+
62
+ corrector.replace(node, correction)
63
+ end
55
64
  end
56
65
  alias on_defs on_def
57
66
 
@@ -98,6 +107,12 @@ module RuboCop
98
107
  def expanded_style?
99
108
  style == :expanded
100
109
  end
110
+
111
+ def max_line_length
112
+ return unless config.for_cop('Layout/LineLength')['Enabled']
113
+
114
+ config.for_cop('Layout/LineLength')['Max']
115
+ end
101
116
  end
102
117
  end
103
118
  end
@@ -51,7 +51,7 @@ module RuboCop
51
51
  text = comment.without(:encoding)
52
52
 
53
53
  if text.blank?
54
- corrector.remove(range_with_surrounding_space(range: range, side: :right))
54
+ corrector.remove(range_with_surrounding_space(range, side: :right))
55
55
  else
56
56
  corrector.replace(range, text)
57
57
  end
@@ -42,7 +42,7 @@ module RuboCop
42
42
  include RangeHelp
43
43
  extend AutoCorrector
44
44
 
45
- MSG = 'Consider using explicit block argument in the '\
45
+ MSG = 'Consider using explicit block argument in the ' \
46
46
  "surrounding method's signature over `yield`."
47
47
 
48
48
  # @!method yielding_block?(node)
@@ -69,6 +69,7 @@ module RuboCop
69
69
  class FormatStringToken < Base
70
70
  include ConfigurableEnforcedStyle
71
71
  include IgnoredMethods
72
+ extend AutoCorrector
72
73
 
73
74
  def on_str(node)
74
75
  return if format_string_token?(node) || use_ignored_method?(node)
@@ -77,13 +78,8 @@ module RuboCop
77
78
  return if detections.empty?
78
79
  return if allowed_unannotated?(detections)
79
80
 
80
- detections.each do |detected_style, token_range|
81
- if detected_style == style
82
- correct_style_detected
83
- else
84
- style_detected(detected_style)
85
- add_offense(token_range, message: message(detected_style))
86
- end
81
+ detections.each do |detected_sequence, token_range|
82
+ check_sequence(detected_sequence, token_range)
87
83
  end
88
84
  end
89
85
 
@@ -106,6 +102,38 @@ module RuboCop
106
102
  send_parent && ignored_method?(send_parent.method_name)
107
103
  end
108
104
 
105
+ def check_sequence(detected_sequence, token_range)
106
+ if detected_sequence.style == style
107
+ correct_style_detected
108
+ elsif correctable_sequence?(detected_sequence.type)
109
+ style_detected(detected_sequence.style)
110
+ add_offense(token_range, message: message(detected_sequence.style)) do |corrector|
111
+ autocorrect_sequence(corrector, detected_sequence, token_range)
112
+ end
113
+ end
114
+ end
115
+
116
+ def correctable_sequence?(detected_type)
117
+ detected_type == 's' || style == :annotated || style == :unannotated
118
+ end
119
+
120
+ def autocorrect_sequence(corrector, detected_sequence, token_range)
121
+ return if style == :unannotated
122
+
123
+ name = detected_sequence.name
124
+ return if name.nil?
125
+
126
+ flags = detected_sequence.flags
127
+ width = detected_sequence.width
128
+ precision = detected_sequence.precision
129
+ type = detected_sequence.style == :template ? 's' : detected_sequence.type
130
+ correction = case style
131
+ when :annotated then "%<#{name}>#{flags}#{width}#{precision}#{type}"
132
+ when :template then "%#{flags}#{width}#{precision}{#{name}}"
133
+ end
134
+ corrector.replace(token_range, correction)
135
+ end
136
+
109
137
  def unannotated_format?(node, detected_style)
110
138
  detected_style == :unannotated && !format_string_in_typical_context?(node)
111
139
  end
@@ -143,30 +171,33 @@ module RuboCop
143
171
  def token_ranges(contents)
144
172
  format_string = RuboCop::Cop::Utils::FormatString.new(contents.source)
145
173
 
146
- format_string.format_sequences.each do |seq|
147
- next if seq.percent?
174
+ format_string.format_sequences.each do |detected_sequence|
175
+ next if detected_sequence.percent?
148
176
 
149
- detected_style = seq.style
150
- token = contents.begin.adjust(begin_pos: seq.begin_pos, end_pos: seq.end_pos)
177
+ token = contents.begin.adjust(begin_pos: detected_sequence.begin_pos,
178
+ end_pos: detected_sequence.end_pos)
151
179
 
152
- yield(detected_style, token)
180
+ yield(detected_sequence, token)
153
181
  end
154
182
  end
155
183
 
156
184
  def collect_detections(node)
157
185
  detections = []
158
- tokens(node) do |detected_style, token_range|
159
- unless unannotated_format?(node, detected_style)
160
- detections << [detected_style, token_range]
186
+ tokens(node) do |detected_sequence, token_range|
187
+ unless unannotated_format?(node, detected_sequence.style)
188
+ detections << [detected_sequence, token_range]
161
189
  end
162
190
  end
163
191
  detections
164
192
  end
165
193
 
166
194
  def allowed_unannotated?(detections)
167
- return false if detections.size > max_unannotated_placeholders_allowed
195
+ return false unless detections.all? do |detected_sequence,|
196
+ detected_sequence.style == :unannotated
197
+ end
198
+ return true if detections.size <= max_unannotated_placeholders_allowed
168
199
 
169
- detections.all? { |detected_style,| detected_style == :unannotated }
200
+ detections.any? { |detected_sequence,| !correctable_sequence?(detected_sequence.type) }
170
201
  end
171
202
 
172
203
  def max_unannotated_placeholders_allowed
@@ -182,7 +182,7 @@ module RuboCop
182
182
  end
183
183
 
184
184
  def remove_comment(corrector, node)
185
- corrector.remove(range_with_surrounding_space(range: node.pos, side: :right))
185
+ corrector.remove(range_with_surrounding_space(node.pos, side: :right))
186
186
  end
187
187
 
188
188
  def enable_comment(corrector)
@@ -110,7 +110,7 @@ module RuboCop
110
110
  kw = if guard_clause_in_if
111
111
  node.loc.keyword.source
112
112
  else
113
- opposite_keyword(node)
113
+ node.inverse_keyword
114
114
  end
115
115
 
116
116
  register_offense(node, guard_clause_source(guard_clause), kw)
@@ -123,7 +123,7 @@ module RuboCop
123
123
  return if allowed_consecutive_conditionals? &&
124
124
  consecutive_conditionals?(node.parent, node)
125
125
 
126
- register_offense(node, 'return', opposite_keyword(node))
126
+ register_offense(node, 'return', node.inverse_keyword)
127
127
  end
128
128
 
129
129
  def consecutive_conditionals?(parent, node)
@@ -134,14 +134,12 @@ module RuboCop
134
134
  end
135
135
  end
136
136
 
137
- def opposite_keyword(node)
138
- node.if? ? 'unless' : 'if'
139
- end
140
-
141
137
  def register_offense(node, scope_exiting_keyword, conditional_keyword)
142
138
  condition, = node.node_parts
143
139
  example = [scope_exiting_keyword, conditional_keyword, condition.source].join(' ')
144
140
  if too_long_for_single_line?(node, example)
141
+ return if trivial?(node)
142
+
145
143
  example = "#{conditional_keyword} #{condition.source}; #{scope_exiting_keyword}; end"
146
144
  end
147
145
 
@@ -167,6 +165,10 @@ module RuboCop
167
165
  accepted_if?(node, ending) || node.condition.multiline? || node.parent&.assignment?
168
166
  end
169
167
 
168
+ def trivial?(node)
169
+ node.branches.one? && !node.if_branch.if_type? && !node.if_branch.begin_type?
170
+ end
171
+
170
172
  def accepted_if?(node, ending)
171
173
  return true if node.modifier_form? || node.ternary?
172
174
 
@@ -87,7 +87,7 @@ module RuboCop
87
87
 
88
88
  def remove_last_element_trailing_comma(corrector, node)
89
89
  range = range_with_surrounding_space(
90
- range: node.children.last.source_range,
90
+ node.children.last.source_range,
91
91
  side: :right
92
92
  ).end.resize(1)
93
93