rubocop 0.74.0 → 0.75.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -1
  3. data/config/default.yml +27 -3
  4. data/lib/rubocop.rb +6 -1
  5. data/lib/rubocop/ast/node/mixin/method_dispatch_node.rb +1 -12
  6. data/lib/rubocop/comment_config.rb +3 -2
  7. data/lib/rubocop/config.rb +4 -0
  8. data/lib/rubocop/config_loader.rb +20 -2
  9. data/lib/rubocop/config_loader_resolver.rb +2 -2
  10. data/lib/rubocop/config_obsoletion.rb +12 -0
  11. data/lib/rubocop/cop/autocorrect_logic.rb +2 -2
  12. data/lib/rubocop/cop/cop.rb +4 -3
  13. data/lib/rubocop/cop/correctors/alignment_corrector.rb +43 -17
  14. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +2 -2
  15. data/lib/rubocop/cop/generator.rb +3 -3
  16. data/lib/rubocop/cop/generator/configuration_injector.rb +9 -4
  17. data/lib/rubocop/cop/generator/require_file_injector.rb +1 -1
  18. data/lib/rubocop/cop/layout/block_alignment.rb +2 -2
  19. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +1 -1
  20. data/lib/rubocop/cop/layout/extra_spacing.rb +0 -6
  21. data/lib/rubocop/cop/layout/indent_assignment.rb +9 -1
  22. data/lib/rubocop/cop/layout/indent_heredoc.rb +1 -1
  23. data/lib/rubocop/cop/layout/multiline_block_layout.rb +24 -2
  24. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +5 -1
  25. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +1 -1
  26. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +7 -0
  27. data/lib/rubocop/cop/layout/space_inside_string_interpolation.rb +2 -0
  28. data/lib/rubocop/cop/lint/assignment_in_condition.rb +17 -4
  29. data/lib/rubocop/cop/lint/debugger.rb +1 -1
  30. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +10 -36
  31. data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
  32. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +91 -0
  33. data/lib/rubocop/cop/lint/unneeded_cop_disable_directive.rb +1 -1
  34. data/lib/rubocop/cop/message_annotator.rb +16 -7
  35. data/lib/rubocop/cop/migration/department_name.rb +44 -0
  36. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  37. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -1
  38. data/lib/rubocop/cop/mixin/safe_mode.rb +2 -0
  39. data/lib/rubocop/cop/naming/method_name.rb +12 -1
  40. data/lib/rubocop/cop/naming/variable_name.rb +1 -0
  41. data/lib/rubocop/cop/offense.rb +18 -7
  42. data/lib/rubocop/cop/registry.rb +22 -1
  43. data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -0
  44. data/lib/rubocop/cop/style/block_delimiters.rb +2 -1
  45. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +29 -10
  46. data/lib/rubocop/cop/style/class_and_module_children.rb +1 -1
  47. data/lib/rubocop/cop/style/commented_keyword.rb +8 -2
  48. data/lib/rubocop/cop/style/conditional_assignment.rb +4 -4
  49. data/lib/rubocop/cop/style/double_cop_disable_directive.rb +8 -2
  50. data/lib/rubocop/cop/style/format_string_token.rb +10 -40
  51. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +18 -33
  52. data/lib/rubocop/cop/style/if_unless_modifier.rb +51 -15
  53. data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
  54. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +5 -5
  55. data/lib/rubocop/cop/style/mixin_usage.rb +11 -1
  56. data/lib/rubocop/cop/style/multiline_memoization.rb +1 -1
  57. data/lib/rubocop/cop/style/nested_modifier.rb +18 -2
  58. data/lib/rubocop/cop/style/or_assignment.rb +6 -1
  59. data/lib/rubocop/cop/style/parentheses_around_condition.rb +14 -0
  60. data/lib/rubocop/cop/style/redundant_parentheses.rb +13 -4
  61. data/lib/rubocop/cop/style/redundant_self.rb +18 -1
  62. data/lib/rubocop/cop/style/rescue_modifier.rb +24 -0
  63. data/lib/rubocop/cop/style/safe_navigation.rb +9 -0
  64. data/lib/rubocop/cop/style/single_line_methods.rb +8 -1
  65. data/lib/rubocop/cop/style/ternary_parentheses.rb +19 -0
  66. data/lib/rubocop/cop/utils/format_string.rb +128 -0
  67. data/lib/rubocop/cop/variable_force/variable.rb +15 -2
  68. data/lib/rubocop/core_ext/string.rb +0 -24
  69. data/lib/rubocop/formatter/emacs_style_formatter.rb +8 -5
  70. data/lib/rubocop/formatter/formatter_set.rb +2 -1
  71. data/lib/rubocop/formatter/pacman_formatter.rb +80 -0
  72. data/lib/rubocop/formatter/simple_text_formatter.rb +9 -1
  73. data/lib/rubocop/formatter/tap_formatter.rb +9 -1
  74. data/lib/rubocop/magic_comment.rb +4 -0
  75. data/lib/rubocop/options.rb +2 -1
  76. data/lib/rubocop/runner.rb +14 -8
  77. data/lib/rubocop/version.rb +1 -1
  78. metadata +6 -3
  79. data/lib/rubocop/cop/mixin/ignored_method_patterns.rb +0 -19
@@ -12,7 +12,6 @@ module RuboCop
12
12
  Description: 'TODO: Write a description of the cop.'
13
13
  Enabled: true
14
14
  VersionAdded: '%<version_added>s'
15
-
16
15
  YAML
17
16
 
18
17
  def initialize(configuration_file_path:, badge:, version_added:)
@@ -23,8 +22,13 @@ module RuboCop
23
22
  end
24
23
 
25
24
  def inject
26
- configuration_entries.insert(find_target_line,
27
- new_configuration_entry)
25
+ target_line = find_target_line
26
+ if target_line
27
+ configuration_entries.insert(find_target_line,
28
+ new_configuration_entry + "\n")
29
+ else
30
+ configuration_entries.push("\n" + new_configuration_entry)
31
+ end
28
32
 
29
33
  File.write(configuration_file_path, configuration_entries.join)
30
34
 
@@ -49,7 +53,8 @@ module RuboCop
49
53
 
50
54
  return index if badge.to_s < line
51
55
  end
52
- configuration_entries.size - 1
56
+
57
+ nil
53
58
  end
54
59
 
55
60
  def cop_name_line?(yaml)
@@ -54,7 +54,7 @@ module RuboCop
54
54
  elsif in_the_same_department
55
55
  break index
56
56
  end
57
- end
57
+ end || require_entries.size
58
58
  end
59
59
  end
60
60
 
@@ -44,8 +44,8 @@ module RuboCop
44
44
  #
45
45
  # foo.bar
46
46
  # .each do
47
- # baz
48
- # end
47
+ # baz
48
+ # end
49
49
  #
50
50
  # @example EnforcedStyleAlignWith: start_of_line
51
51
  # # bad
@@ -191,7 +191,7 @@ module RuboCop
191
191
  end
192
192
 
193
193
  def indentation_width
194
- @config.for_cop('IndentationWidth')['Width'] || 2
194
+ @config.for_cop('Layout/IndentationWidth')['Width'] || 2
195
195
  end
196
196
 
197
197
  def line_break_after_left_paren?(left_paren, elements)
@@ -82,9 +82,7 @@ module RuboCop
82
82
  token2.text.start_with?('#')
83
83
 
84
84
  extra_space_range(token1, token2) do |range|
85
- # Unary + doesn't appear as a token and needs special handling.
86
85
  next if ignored_range?(ast, range.begin_pos)
87
- next if unary_plus_non_offense?(range)
88
86
 
89
87
  add_offense(range, location: range, message: MSG_UNNECESSARY)
90
88
  end
@@ -114,10 +112,6 @@ module RuboCop
114
112
  ignored_ranges(ast).any? { |r| r.include?(start_pos) }
115
113
  end
116
114
 
117
- def unary_plus_non_offense?(range)
118
- range.resize(range.size + 1).source =~ /^ ?\+$/
119
- end
120
-
121
115
  # Returns an array of ranges that should not be reported. It's the
122
116
  # extra spaces between the keys and values in a multiline hash,
123
117
  # since those are handled by the Style/AlignHash cop.
@@ -33,13 +33,21 @@ module RuboCop
33
33
  return unless node.loc.operator
34
34
  return if node.loc.operator.line == rhs.first_line
35
35
 
36
- base = display_column(node.source_range)
36
+ base = display_column(leftmost_multiple_assignment(node).source_range)
37
37
  check_alignment([rhs], base + configured_indentation_width)
38
38
  end
39
39
 
40
40
  def autocorrect(node)
41
41
  AlignmentCorrector.correct(processed_source, node, column_delta)
42
42
  end
43
+
44
+ def leftmost_multiple_assignment(node)
45
+ return node unless node.parent&.assignment?
46
+
47
+ leftmost_multiple_assignment(node.parent)
48
+
49
+ node.parent
50
+ end
43
51
  end
44
52
  end
45
53
  end
@@ -240,7 +240,7 @@ module RuboCop
240
240
  end
241
241
 
242
242
  def indentation_width
243
- @config.for_cop('IndentationWidth')['Width'] || 2
243
+ @config.for_cop('Layout/IndentationWidth')['Width'] || 2
244
244
  end
245
245
 
246
246
  def heredoc_body(node)
@@ -5,7 +5,9 @@ module RuboCop
5
5
  module Layout
6
6
  # This cop checks whether the multiline do end blocks have a newline
7
7
  # after the start of the block. Additionally, it checks whether the block
8
- # arguments, if any, are on the same line as the start of the block.
8
+ # arguments, if any, are on the same line as the start of the
9
+ # block. Putting block arguments on separate lines, because the whole
10
+ # line would otherwise be too long, is accepted.
9
11
  #
10
12
  # @example
11
13
  # # bad
@@ -35,6 +37,17 @@ module RuboCop
35
37
  # foo(i)
36
38
  # bar(i)
37
39
  # }
40
+ #
41
+ # # good
42
+ # blah { |
43
+ # long_list,
44
+ # of_parameters,
45
+ # that_would_not,
46
+ # fit_on_one_line
47
+ # |
48
+ # foo(i)
49
+ # bar(i)
50
+ # }
38
51
  class MultilineBlockLayout < Cop
39
52
  include RangeHelp
40
53
 
@@ -42,11 +55,13 @@ module RuboCop
42
55
  'the block start.'
43
56
  ARG_MSG = 'Block argument expression is not on the same line as the ' \
44
57
  'block start.'
58
+ PIPE_SIZE = '|'.length
45
59
 
46
60
  def on_block(node)
47
61
  return if node.single_line?
48
62
 
49
- unless args_on_beginning_line?(node)
63
+ unless args_on_beginning_line?(node) ||
64
+ line_break_necessary_in_args?(node)
50
65
  add_offense_for_expression(node, node.arguments, ARG_MSG)
51
66
  end
52
67
 
@@ -79,6 +94,13 @@ module RuboCop
79
94
  node.loc.begin.line == node.arguments.loc.last_line
80
95
  end
81
96
 
97
+ def line_break_necessary_in_args?(node)
98
+ needed_length = node.source_range.column +
99
+ node.source.lines.first.length +
100
+ block_arg_string(node.arguments).length + PIPE_SIZE
101
+ needed_length > max_line_length
102
+ end
103
+
82
104
  def add_offense_for_expression(node, expr, msg)
83
105
  expression = expr.source_range
84
106
  range = range_between(expression.begin_pos, expression.end_pos)
@@ -3,7 +3,9 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Layout
6
- # Checks the spacing inside and after block parameters pipes.
6
+ # Checks the spacing inside and after block parameters pipes. Line breaks
7
+ # inside parameter pipes are checked by `Layout/MultilineBlockLayout` and
8
+ # not by this cop.
7
9
  #
8
10
  # @example EnforcedStyleInsidePipes: no_space (default)
9
11
  # # bad
@@ -156,6 +158,8 @@ module RuboCop
156
158
  return if space_begin_pos >= space_end_pos
157
159
 
158
160
  range = range_between(space_begin_pos, space_end_pos)
161
+ return if range.source.include?("\n")
162
+
159
163
  add_offense(range, location: range,
160
164
  message: "#{msg} block parameter detected.")
161
165
  end
@@ -132,7 +132,7 @@ module RuboCop
132
132
  line, col = line_and_column_for(token)
133
133
  return true if col == -1
134
134
 
135
- processed_source.lines[line][0..col].delete(' ').empty?
135
+ processed_source.lines[line][0..col] !~ /\S/
136
136
  end
137
137
 
138
138
  def index_for(node, token)
@@ -84,6 +84,13 @@ module RuboCop
84
84
  def on_block(node)
85
85
  return if node.keywords?
86
86
 
87
+ # Do not register an offense for multi-line empty braces. That means
88
+ # preventing auto-correction to single-line empty braces. It will
89
+ # conflict with auto-correction by `Layout/SpaceInsideBlockBraces` cop
90
+ # if auto-corrected to a single-line empty braces.
91
+ # See: https://github.com/rubocop-hq/rubocop/issues/7363
92
+ return if node.body.nil? && node.multiline?
93
+
87
94
  left_brace = node.loc.begin
88
95
  right_brace = node.loc.end
89
96
 
@@ -28,6 +28,8 @@ module RuboCop
28
28
  SPACE_MSG = 'Missing space inside string interpolation detected.'
29
29
 
30
30
  def on_interpolation(begin_node)
31
+ return if begin_node.multiline?
32
+
31
33
  delims = delimiters(begin_node)
32
34
  return if empty_brackets?(*delims)
33
35
 
@@ -6,21 +6,34 @@ module RuboCop
6
6
  # This cop checks for assignments in the conditions of
7
7
  # if/while/until.
8
8
  #
9
- # @example
9
+ # `AllowSafeAssignment` option for safe assignment.
10
+ # By safe assignment we mean putting parentheses around
11
+ # an assignment to indicate "I know I'm using an assignment
12
+ # as a condition. It's not a mistake."
10
13
  #
14
+ # @example
11
15
  # # bad
12
- #
13
16
  # if some_var = true
14
17
  # do_something
15
18
  # end
16
19
  #
17
- # @example
20
+ # # good
21
+ # if some_var == true
22
+ # do_something
23
+ # end
18
24
  #
25
+ # @example AllowSafeAssignment: true (default)
19
26
  # # good
27
+ # if (some_var = true)
28
+ # do_something
29
+ # end
20
30
  #
21
- # if some_var == true
31
+ # @example AllowSafeAssignment: false
32
+ # # bad
33
+ # if (some_var = true)
22
34
  # do_something
23
35
  # end
36
+ #
24
37
  class AssignmentInCondition < Cop
25
38
  include SafeAssignment
26
39
 
@@ -43,7 +43,7 @@ module RuboCop
43
43
  PATTERN
44
44
 
45
45
  def_node_matcher :debugger_call?, <<~PATTERN
46
- {(send {nil? #kernel?} {:debugger :byebug} ...)
46
+ {(send {nil? #kernel?} {:debugger :byebug :remote_byebug} ...)
47
47
  (send (send {#kernel? nil?} :binding)
48
48
  {:pry :remote_pry :pry_remote} ...)
49
49
  (send (const {nil? (cbase)} :Pry) :rescue ...)
@@ -22,16 +22,10 @@ module RuboCop
22
22
  # http://rubular.com/r/CvpbxkcTzy
23
23
  MSG = "Number of arguments (%<arg_num>i) to `%<method>s` doesn't " \
24
24
  'match the number of fields (%<field_num>i).'
25
- FIELD_REGEX =
26
- /(%(([\s#+-0\*]*)(\d*)?(\.\d+)?[bBdiouxXeEfgGaAcps]|%))/.freeze
27
- NAMED_FIELD_REGEX = /%\{[_a-zA-Z][_a-zA-Z]+\}/.freeze
25
+
28
26
  KERNEL = 'Kernel'
29
27
  SHOVEL = '<<'
30
- PERCENT = '%'
31
- PERCENT_PERCENT = '%%'
32
- DIGIT_DOLLAR_FLAG = /%(\d+)\$/.freeze
33
28
  STRING_TYPES = %i[str dstr].freeze
34
- NAMED_INTERPOLATION = /%(?:<\w+>|\{\w+\})/.freeze
35
29
 
36
30
  def on_send(node)
37
31
  return unless offending_node?(node)
@@ -44,7 +38,7 @@ module RuboCop
44
38
  def offending_node?(node)
45
39
  return false unless called_on_string?(node)
46
40
  return false unless method_with_format_args?(node)
47
- return false if named_mode?(node) || splat_args?(node)
41
+ return false if splat_args?(node)
48
42
 
49
43
  num_of_format_args, num_of_expected_fields = count_matches(node)
50
44
 
@@ -70,16 +64,6 @@ module RuboCop
70
64
  sprintf?(node) || format?(node) || percent?(node)
71
65
  end
72
66
 
73
- def named_mode?(node)
74
- relevant_node = if sprintf?(node) || format?(node)
75
- node.first_argument
76
- elsif percent?(node)
77
- node.receiver
78
- end
79
-
80
- !relevant_node.source.scan(NAMED_FIELD_REGEX).empty?
81
- end
82
-
83
67
  def splat_args?(node)
84
68
  return false if percent?(node)
85
69
 
@@ -127,27 +111,17 @@ module RuboCop
127
111
 
128
112
  def expected_fields_count(node)
129
113
  return :unknown unless node.str_type?
130
- return 1 if node.source =~ NAMED_INTERPOLATION
131
114
 
132
- max_digit_dollar_num = max_digit_dollar_num(node)
133
- return max_digit_dollar_num if max_digit_dollar_num&.nonzero?
115
+ format_string = RuboCop::Cop::Utils::FormatString.new(node.source)
116
+ return 1 if format_string.named_interpolation?
134
117
 
135
- node
136
- .source
137
- .scan(FIELD_REGEX)
138
- .reject { |x| x.first == PERCENT_PERCENT }
139
- .reduce(0) { |acc, elem| acc + arguments_count(elem[2]) }
140
- end
141
-
142
- def max_digit_dollar_num(node)
143
- node.source.scan(DIGIT_DOLLAR_FLAG).map do |digit_dollar_num|
144
- digit_dollar_num.first.to_i
145
- end.max
146
- end
118
+ max_digit_dollar_num = format_string.max_digit_dollar_num
119
+ return max_digit_dollar_num if max_digit_dollar_num&.nonzero?
147
120
 
148
- # number of arguments required for the format sequence
149
- def arguments_count(format)
150
- format.scan('*').count + 1
121
+ format_string
122
+ .format_sequences
123
+ .reject(&:percent?)
124
+ .reduce(0) { |acc, seq| acc + seq.arity }
151
125
  end
152
126
 
153
127
  def format?(node)
@@ -41,7 +41,7 @@ module RuboCop
41
41
 
42
42
  def on_send(node)
43
43
  to_method(node) do |receiver, to_method|
44
- next if date_time_object?(receiver)
44
+ next if receiver.nil? || date_time_object?(receiver)
45
45
 
46
46
  message = format(
47
47
  MSG,
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ #
7
+ # This cop checks for `send`, `public_send`, and `__send__` methods
8
+ # when using mix-in.
9
+ #
10
+ # `include` and `prepend` methods were private methods until Ruby 2.0,
11
+ # they were mixed-in via `send` method. This cop uses Ruby 2.1 or
12
+ # higher style that can be called by public methods.
13
+ # And `extend` method that was originally a public method is also targeted
14
+ # for style unification.
15
+ #
16
+ # @example
17
+ # # bad
18
+ # Foo.send(:include, Bar)
19
+ # Foo.send(:prepend, Bar)
20
+ # Foo.send(:extend, Bar)
21
+ #
22
+ # # bad
23
+ # Foo.public_send(:include, Bar)
24
+ # Foo.public_send(:prepend, Bar)
25
+ # Foo.public_send(:extend, Bar)
26
+ #
27
+ # # bad
28
+ # Foo.__send__(:include, Bar)
29
+ # Foo.__send__(:prepend, Bar)
30
+ # Foo.__send__(:extend, Bar)
31
+ #
32
+ # # good
33
+ # Foo.include Bar
34
+ # Foo.prepend Bar
35
+ # Foo.extend Bar
36
+ #
37
+ class SendWithMixinArgument < Cop
38
+ include RangeHelp
39
+
40
+ MSG = 'Use `%<method>s %<module_name>s` instead of `%<bad_method>s`.'
41
+ MIXIN_METHODS = %i[include prepend extend].freeze
42
+
43
+ def_node_matcher :send_with_mixin_argument?, <<~PATTERN
44
+ (send
45
+ (const _ _) {:send :public_send :__send__}
46
+ ({sym str} $#mixin_method?)
47
+ $(const _ _))
48
+ PATTERN
49
+
50
+ def on_send(node)
51
+ send_with_mixin_argument?(node) do |method, module_name|
52
+ message = message(
53
+ method, module_name.source, bad_location(node).source
54
+ )
55
+
56
+ add_offense(node, location: bad_location(node), message: message)
57
+ end
58
+ end
59
+
60
+ def autocorrect(node)
61
+ send_with_mixin_argument?(node) do |method, module_name|
62
+ lambda do |corrector|
63
+ corrector.replace(
64
+ bad_location(node), "#{method} #{module_name.source}"
65
+ )
66
+ end
67
+ end
68
+ end
69
+
70
+ private
71
+
72
+ def bad_location(node)
73
+ loc = node.loc
74
+
75
+ range_between(loc.selector.begin_pos, loc.expression.end_pos)
76
+ end
77
+
78
+ def message(method, module_name, bad_method)
79
+ format(
80
+ MSG,
81
+ method: method, module_name: module_name, bad_method: bad_method
82
+ )
83
+ end
84
+
85
+ def mixin_method?(node)
86
+ MIXIN_METHODS.include?(node.to_sym)
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end