rubocop 1.30.1 → 1.31.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (132) 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_loader_resolver.rb +1 -1
  11. data/lib/rubocop/config_validator.rb +3 -3
  12. data/lib/rubocop/cop/base.rb +5 -1
  13. data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -1
  14. data/lib/rubocop/cop/bundler/gem_filename.rb +4 -4
  15. data/lib/rubocop/cop/bundler/ordered_gems.rb +2 -2
  16. data/lib/rubocop/cop/corrector.rb +2 -2
  17. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +3 -3
  18. data/lib/rubocop/cop/correctors/unused_arg_corrector.rb +1 -1
  19. data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +31 -16
  20. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +1 -1
  21. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
  22. data/lib/rubocop/cop/gemspec/require_mfa.rb +20 -20
  23. data/lib/rubocop/cop/generator.rb +5 -1
  24. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -5
  25. data/lib/rubocop/cop/internal_affairs/redundant_location_argument.rb +1 -1
  26. data/lib/rubocop/cop/internal_affairs/redundant_message_argument.rb +1 -1
  27. data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +54 -0
  28. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  29. data/lib/rubocop/cop/layout/empty_comment.rb +1 -1
  30. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +25 -4
  31. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +20 -13
  32. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +51 -12
  33. data/lib/rubocop/cop/layout/initial_indentation.rb +1 -1
  34. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +68 -0
  35. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +130 -0
  36. data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -1
  37. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +2 -2
  38. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -2
  39. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +1 -1
  40. data/lib/rubocop/cop/layout/space_around_operators.rb +1 -1
  41. data/lib/rubocop/cop/layout/space_before_block_braces.rb +1 -1
  42. data/lib/rubocop/cop/layout/space_before_first_arg.rb +1 -1
  43. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +5 -3
  44. data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -1
  45. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +51 -0
  46. data/lib/rubocop/cop/lint/interpolation_check.rb +1 -1
  47. data/lib/rubocop/cop/lint/literal_as_condition.rb +5 -0
  48. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +126 -0
  49. data/lib/rubocop/cop/lint/number_conversion.rb +3 -3
  50. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -1
  51. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +5 -5
  52. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +1 -1
  53. data/lib/rubocop/cop/lint/redundant_require_statement.rb +1 -1
  54. data/lib/rubocop/cop/lint/regexp_as_condition.rb +2 -2
  55. data/lib/rubocop/cop/lint/struct_new_override.rb +2 -2
  56. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -1
  57. data/lib/rubocop/cop/lint/useless_else_without_rescue.rb +44 -0
  58. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  59. data/lib/rubocop/cop/mixin/def_node.rb +2 -7
  60. data/lib/rubocop/cop/mixin/multiline_element_indentation.rb +34 -12
  61. data/lib/rubocop/cop/mixin/range_help.rb +7 -3
  62. data/lib/rubocop/cop/style/accessor_grouping.rb +1 -1
  63. data/lib/rubocop/cop/style/arguments_forwarding.rb +1 -1
  64. data/lib/rubocop/cop/style/block_delimiters.rb +4 -2
  65. data/lib/rubocop/cop/style/commented_keyword.rb +1 -1
  66. data/lib/rubocop/cop/style/conditional_assignment.rb +1 -0
  67. data/lib/rubocop/cop/style/empty_method.rb +16 -1
  68. data/lib/rubocop/cop/style/encoding.rb +1 -1
  69. data/lib/rubocop/cop/style/explicit_block_argument.rb +1 -1
  70. data/lib/rubocop/cop/style/format_string_token.rb +48 -17
  71. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +1 -1
  72. data/lib/rubocop/cop/style/guard_clause.rb +8 -6
  73. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +1 -1
  74. data/lib/rubocop/cop/style/hash_except.rb +88 -8
  75. data/lib/rubocop/cop/style/hash_syntax.rb +2 -2
  76. data/lib/rubocop/cop/style/implicit_runtime_error.rb +2 -2
  77. data/lib/rubocop/cop/style/keyword_parameters_order.rb +1 -1
  78. data/lib/rubocop/cop/style/line_end_concatenation.rb +1 -1
  79. data/lib/rubocop/cop/style/map_to_hash.rb +1 -1
  80. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -2
  81. data/lib/rubocop/cop/style/module_function.rb +2 -2
  82. data/lib/rubocop/cop/style/multiline_if_modifier.rb +2 -2
  83. data/lib/rubocop/cop/style/multiline_if_then.rb +1 -1
  84. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +1 -3
  85. data/lib/rubocop/cop/style/multiline_method_signature.rb +1 -1
  86. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +1 -1
  87. data/lib/rubocop/cop/style/multiline_when_then.rb +1 -3
  88. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +1 -1
  89. data/lib/rubocop/cop/style/nested_ternary_operator.rb +19 -7
  90. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  91. data/lib/rubocop/cop/style/not.rb +1 -1
  92. data/lib/rubocop/cop/style/redundant_argument.rb +1 -1
  93. data/lib/rubocop/cop/style/redundant_parentheses.rb +2 -1
  94. data/lib/rubocop/cop/style/redundant_return.rb +1 -1
  95. data/lib/rubocop/cop/style/redundant_self_assignment.rb +1 -1
  96. data/lib/rubocop/cop/style/sole_nested_conditional.rb +2 -2
  97. data/lib/rubocop/cop/style/struct_inheritance.rb +2 -2
  98. data/lib/rubocop/cop/style/swap_values.rb +1 -1
  99. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  100. data/lib/rubocop/cop/style/top_level_method_definition.rb +0 -2
  101. data/lib/rubocop/cop/style/unpack_first.rb +1 -1
  102. data/lib/rubocop/cop/util.rb +1 -1
  103. data/lib/rubocop/formatter/formatter_set.rb +20 -19
  104. data/lib/rubocop/formatter/git_hub_actions_formatter.rb +15 -2
  105. data/lib/rubocop/formatter/html_formatter.rb +0 -1
  106. data/lib/rubocop/formatter/offense_count_formatter.rb +2 -0
  107. data/lib/rubocop/formatter/simple_text_formatter.rb +6 -7
  108. data/lib/rubocop/formatter.rb +31 -0
  109. data/lib/rubocop/options.rb +24 -1
  110. data/lib/rubocop/rake_task.rb +34 -9
  111. data/lib/rubocop/server/cache.rb +109 -0
  112. data/lib/rubocop/server/cli.rb +104 -0
  113. data/lib/rubocop/server/client_command/base.rb +44 -0
  114. data/lib/rubocop/server/client_command/exec.rb +59 -0
  115. data/lib/rubocop/server/client_command/restart.rb +25 -0
  116. data/lib/rubocop/server/client_command/start.rb +43 -0
  117. data/lib/rubocop/server/client_command/status.rb +28 -0
  118. data/lib/rubocop/server/client_command/stop.rb +31 -0
  119. data/lib/rubocop/server/client_command.rb +26 -0
  120. data/lib/rubocop/server/core.rb +79 -0
  121. data/lib/rubocop/server/errors.rb +23 -0
  122. data/lib/rubocop/server/helper.rb +34 -0
  123. data/lib/rubocop/server/server_command/base.rb +50 -0
  124. data/lib/rubocop/server/server_command/exec.rb +34 -0
  125. data/lib/rubocop/server/server_command/stop.rb +24 -0
  126. data/lib/rubocop/server/server_command.rb +21 -0
  127. data/lib/rubocop/server/socket_reader.rb +65 -0
  128. data/lib/rubocop/server.rb +53 -0
  129. data/lib/rubocop/version.rb +15 -8
  130. data/lib/rubocop.rb +8 -27
  131. metadata +42 -4
  132. data/lib/rubocop/cop/gemspec/date_assignment.rb +0 -49
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # Checks for non-atomic file operation.
7
+ # And then replace it with a nearly equivalent and atomic method.
8
+ #
9
+ # These can cause problems that are difficult to reproduce,
10
+ # especially in cases of frequent file operations in parallel,
11
+ # such as test runs with parallel_rspec.
12
+ #
13
+ # For examples: creating a directory if there is none, has the following problems
14
+ #
15
+ # An exception occurs when the directory didn't exist at the time of `exist?`,
16
+ # but someone else created it before `mkdir` was executed.
17
+ #
18
+ # Subsequent processes are executed without the directory that should be there
19
+ # when the directory existed at the time of `exist?`,
20
+ # but someone else deleted it shortly afterwards.
21
+ #
22
+ # @safety
23
+ # This cop is unsafe, because autocorrection change to atomic processing.
24
+ # The atomic processing of the replacement destination is not guaranteed
25
+ # to be strictly equivalent to that before the replacement.
26
+ #
27
+ # @example
28
+ # # bad
29
+ # unless FileTest.exist?(path)
30
+ # FileUtils.makedirs(path)
31
+ # end
32
+ #
33
+ # if FileTest.exist?(path)
34
+ # FileUtils.remove(path)
35
+ # end
36
+ #
37
+ # # good
38
+ # FileUtils.mkdir_p(path)
39
+ #
40
+ # FileUtils.rm_rf(path)
41
+ #
42
+ class NonAtomicFileOperation < Base
43
+ extend AutoCorrector
44
+ include Alignment
45
+ include RangeHelp
46
+
47
+ MSG = 'Remove unnecessary existence checks `%<receiver>s.%<method_name>s`.'
48
+ MAKE_METHODS = %i[makedirs mkdir mkdir_p mkpath].freeze
49
+ REMOVE_METHODS = %i[remove remove_dir remove_entry remove_entry_secure delete unlink
50
+ remove_file rm rm_f rm_r rm_rf rmdir rmtree safe_unlink].freeze
51
+ RESTRICT_ON_SEND = (MAKE_METHODS + REMOVE_METHODS).freeze
52
+
53
+ # @!method send_exist_node(node)
54
+ def_node_search :send_exist_node, <<-PATTERN
55
+ $(send (const nil? {:FileTest :File :Dir :Shell}) {:exist? :exists?} ...)
56
+ PATTERN
57
+
58
+ # @!method receiver_and_method_name(node)
59
+ def_node_matcher :receiver_and_method_name, <<-PATTERN
60
+ (send (const nil? $_) $_ ...)
61
+ PATTERN
62
+
63
+ # @!method force?(node)
64
+ def_node_search :force?, <<~PATTERN
65
+ (pair (sym :force) (:true))
66
+ PATTERN
67
+
68
+ # @!method explicit_not_force?(node)
69
+ def_node_search :explicit_not_force?, <<~PATTERN
70
+ (pair (sym :force) (:false))
71
+ PATTERN
72
+
73
+ def on_send(node)
74
+ return unless (parent = node.parent) && parent.if_type?
75
+ return if allowable_use_with_if?(parent)
76
+ return if explicit_not_force?(node)
77
+ return unless (exist_node = send_exist_node(parent).first)
78
+ return unless exist_node.first_argument == node.first_argument
79
+
80
+ register_offense(node, exist_node)
81
+ end
82
+
83
+ private
84
+
85
+ def allowable_use_with_if?(if_node)
86
+ if_node.condition.and_type? || if_node.condition.or_type? || if_node.else_branch
87
+ end
88
+
89
+ def register_offense(node, exist_node)
90
+ range = range_between(node.parent.loc.keyword.begin_pos,
91
+ exist_node.loc.expression.end_pos)
92
+
93
+ add_offense(range, message: message(exist_node)) do |corrector|
94
+ autocorrect(corrector, node, range)
95
+ end
96
+ end
97
+
98
+ def message(node)
99
+ receiver, method_name = receiver_and_method_name(node)
100
+ format(MSG, receiver: receiver, method_name: method_name)
101
+ end
102
+
103
+ def autocorrect(corrector, node, range)
104
+ corrector.remove(range)
105
+ corrector.replace(node.child_nodes.first.loc.name, 'FileUtils')
106
+ corrector.replace(node.loc.selector, replacement_method(node))
107
+ corrector.remove(node.parent.loc.end) if node.parent.multiline?
108
+ end
109
+
110
+ def replacement_method(node)
111
+ return node.method_name if force_option?(node)
112
+
113
+ if MAKE_METHODS.include?(node.method_name)
114
+ 'mkdir_p'
115
+ elsif REMOVE_METHODS.include?(node.method_name)
116
+ 'rm_rf'
117
+ end
118
+ end
119
+
120
+ def force_option?(node)
121
+ node.arguments.any? { |arg| force?(arg) }
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -64,9 +64,9 @@ module RuboCop
64
64
  to_c: "#{Complex.name}(%<number_object>s)",
65
65
  to_r: "#{Rational.name}(%<number_object>s)"
66
66
  }.freeze
67
- MSG = 'Replace unsafe number conversion with number '\
68
- 'class parsing, instead of using '\
69
- '`%<current>s`, use stricter '\
67
+ MSG = 'Replace unsafe number conversion with number ' \
68
+ 'class parsing, instead of using ' \
69
+ '`%<current>s`, use stricter ' \
70
70
  '`%<corrected_method>s`.'
71
71
  CONVERSION_METHODS = %i[Integer Float Complex Rational to_i to_f to_c to_r].freeze
72
72
  METHODS = CONVERSION_METHOD_CLASS_MAPPING.keys.map(&:inspect).join(' ')
@@ -55,7 +55,7 @@ module RuboCop
55
55
 
56
56
  def chained_calls?(node)
57
57
  first_argument = node.first_argument
58
- first_argument.send_type? && (node.children.last&.children&.count || 0) > 1
58
+ first_argument.call_type? && (node.children.last&.children&.count || 0) > 1
59
59
  end
60
60
 
61
61
  def ternary_expression?(node)
@@ -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
@@ -5,8 +5,7 @@ module RuboCop
5
5
  # Common functionality for checking def nodes.
6
6
  module DefNode
7
7
  extend NodePattern::Macros
8
-
9
- NON_PUBLIC_MODIFIERS = %w[private protected].freeze
8
+ include VisibilityHelp
10
9
 
11
10
  private
12
11
 
@@ -15,11 +14,7 @@ module RuboCop
15
14
  end
16
15
 
17
16
  def preceding_non_public_modifier?(node)
18
- stripped_source_upto(node.first_line).any? { |line| NON_PUBLIC_MODIFIERS.include?(line) }
19
- end
20
-
21
- def stripped_source_upto(index)
22
- processed_source[0..index].map(&:strip)
17
+ node_visibility(node) != :public
23
18
  end
24
19
 
25
20
  # @!method non_public_modifier?(node)
@@ -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, first, 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,34 @@ 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, first, 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, first)
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, first)
67
+ return unless first && first.parent.loc.begin == left_brace
68
+
69
+ first.parent&.parent&.pair_type? ? first.parent.parent : nil
70
+ end
71
+
72
+ def key_and_value_begin_on_same_line?(pair)
73
+ same_line?(pair.key, pair.value)
74
+ end
75
+
76
+ def right_sibling_begins_on_subsequent_line?(pair)
77
+ pair.right_sibling && (pair.last_line < pair.right_sibling.first_line)
56
78
  end
57
79
 
58
80
  def detected_styles(actual_column, offset, left_parenthesis, left_brace)
@@ -73,8 +95,8 @@ module RuboCop
73
95
  styles
74
96
  end
75
97
 
76
- def incorrect_style_detected(styles, first, left_parenthesis)
77
- msg = message(base_description(left_parenthesis))
98
+ def incorrect_style_detected(styles, first, base_column_type)
99
+ msg = message(base_description(base_column_type))
78
100
 
79
101
  add_offense(first, message: msg) do |corrector|
80
102
  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)