rubocop 0.66.0 → 0.67.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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +75 -26
  4. data/exe/rubocop +12 -0
  5. data/lib/rubocop.rb +12 -5
  6. data/lib/rubocop/cli.rb +11 -9
  7. data/lib/rubocop/config.rb +3 -0
  8. data/lib/rubocop/config_loader_resolver.rb +2 -2
  9. data/lib/rubocop/cop/commissioner.rb +3 -3
  10. data/lib/rubocop/cop/layout/multiline_array_line_breaks.rb +39 -0
  11. data/lib/rubocop/cop/layout/multiline_hash_key_line_breaks.rb +50 -0
  12. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +52 -0
  13. data/lib/rubocop/cop/lint/to_json.rb +5 -2
  14. data/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb +33 -0
  15. data/lib/rubocop/cop/mixin/uncommunicative_name.rb +6 -1
  16. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +76 -0
  17. data/lib/rubocop/cop/rails/active_record_override.rb +67 -0
  18. data/lib/rubocop/cop/rails/blank.rb +6 -0
  19. data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +30 -4
  20. data/lib/rubocop/cop/rails/link_to_blank.rb +7 -6
  21. data/lib/rubocop/cop/rails/present.rb +5 -1
  22. data/lib/rubocop/cop/rails/redundant_allow_nil.rb +105 -0
  23. data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +1 -0
  24. data/lib/rubocop/cop/style/block_comments.rb +9 -1
  25. data/lib/rubocop/cop/{performance → style}/redundant_sort_by.rb +1 -1
  26. data/lib/rubocop/cop/{performance → style}/sample.rb +1 -1
  27. data/lib/rubocop/cop/style/stderr_puts.rb +9 -3
  28. data/lib/rubocop/cop/{performance/lstrip_rstrip.rb → style/strip.rb} +2 -2
  29. data/lib/rubocop/cop/style/struct_inheritance.rb +10 -2
  30. data/lib/rubocop/cop/{performance → style}/unneeded_sort.rb +1 -1
  31. data/lib/rubocop/node_pattern.rb +8 -6
  32. data/lib/rubocop/path_util.rb +3 -3
  33. data/lib/rubocop/processed_source.rb +2 -2
  34. data/lib/rubocop/remote_config.rb +6 -4
  35. data/lib/rubocop/result_cache.rb +2 -2
  36. data/lib/rubocop/runner.rb +2 -2
  37. data/lib/rubocop/target_finder.rb +7 -2
  38. data/lib/rubocop/version.rb +1 -1
  39. metadata +29 -7
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Layout
6
+ # This cop ensures that each key in a multi-line hash
7
+ # starts on a separate line.
8
+ #
9
+ # @example
10
+ #
11
+ # # bad
12
+ # {
13
+ # a: 1, b: 2,
14
+ # c: 3
15
+ # }
16
+ #
17
+ # # good
18
+ # {
19
+ # a: 1,
20
+ # b: 2,
21
+ # c: 3
22
+ # }
23
+ class MultilineHashKeyLineBreaks < Cop
24
+ include MultilineElementLineBreaks
25
+
26
+ MSG = 'Each key in a multi-line hash must start on a ' \
27
+ 'separate line.'.freeze
28
+
29
+ def on_hash(node)
30
+ # This cop only deals with hashes wrapped by a set of curly
31
+ # braces like {foo: 1}. That is, not a kwargs hashes.
32
+ # Style/MultilineMethodArgumentLineBreaks handles those.
33
+ return unless starts_with_curly_brace?(node)
34
+
35
+ check_line_breaks(node, node.children) if node.loc.begin
36
+ end
37
+
38
+ def autocorrect(node)
39
+ EmptyLineCorrector.insert_before(node)
40
+ end
41
+
42
+ private
43
+
44
+ def starts_with_curly_brace?(node)
45
+ node.loc.begin
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Layout
6
+ # This cop ensures that each argument in a multi-line method call
7
+ # starts on a separate line.
8
+ #
9
+ # @example
10
+ #
11
+ # # bad
12
+ # foo(a, b,
13
+ # c
14
+ # )
15
+ #
16
+ # # good
17
+ # foo(
18
+ # a,
19
+ # b,
20
+ # c
21
+ # )
22
+ class MultilineMethodArgumentLineBreaks < Cop
23
+ include(MultilineElementLineBreaks)
24
+
25
+ MSG = 'Each argument in a multi-line method call must start ' \
26
+ 'on a separate line.'.freeze
27
+
28
+ def on_send(node)
29
+ args = node.arguments
30
+
31
+ # If there is a trailing hash arg without explicit braces, like this:
32
+ #
33
+ # method(1, 'key1' => value1, 'key2' => value2)
34
+ #
35
+ # ...then each key/value pair is treated as a method 'argument'
36
+ # when determining where line breaks should appear.
37
+ if (last_arg = args.last)
38
+ if last_arg.hash_type? && !last_arg.braces?
39
+ args = args.concat(args.pop.children)
40
+ end
41
+ end
42
+
43
+ check_line_breaks(node, args)
44
+ end
45
+
46
+ def autocorrect(node)
47
+ EmptyLineCorrector.insert_before(node)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -14,7 +14,7 @@ module RuboCop
14
14
  # end
15
15
  #
16
16
  # # good
17
- # def to_json(_opts)
17
+ # def to_json(*_args)
18
18
  # end
19
19
  #
20
20
  class ToJSON < Cop
@@ -29,7 +29,10 @@ module RuboCop
29
29
 
30
30
  def autocorrect(node)
31
31
  lambda do |corrector|
32
- corrector.insert_after(node.loc.name, '(_opts)')
32
+ # The following used `*_args` because `to_json(*args)` has
33
+ # an offense of `Lint/UnusedMethodArgument` cop if `*args`
34
+ # is not used.
35
+ corrector.insert_after(node.loc.name, '(*_args)')
33
36
  end
34
37
  end
35
38
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # TEAM: backend_infra
4
+ # WATCHERS: maxh
5
+
6
+ module RuboCop
7
+ module Cop
8
+ # Common functionality for checking for a line break before each
9
+ # element in a multi-line collection.
10
+ module MultilineElementLineBreaks
11
+ private
12
+
13
+ def check_line_breaks(_node, children)
14
+ return if all_on_same_line?(children)
15
+
16
+ last_seen_line = -1
17
+ children.each do |child|
18
+ if last_seen_line >= child.first_line
19
+ add_offense(child)
20
+ else
21
+ last_seen_line = child.last_line
22
+ end
23
+ end
24
+ end
25
+
26
+ def all_on_same_line?(nodes)
27
+ return true if nodes.empty?
28
+
29
+ nodes.first.first_line == nodes.last.last_line
30
+ end
31
+ end
32
+ end
33
+ end
@@ -13,7 +13,12 @@ module RuboCop
13
13
 
14
14
  def check(node, args)
15
15
  args.each do |arg|
16
- name = arg.children.first.to_s
16
+ # Argument names might be "_" or prefixed with "_" to indicate they
17
+ # are unused. Trim away this prefix and only analyse the basename.
18
+ full_name = arg.children.first.to_s
19
+ next if full_name == '_'
20
+
21
+ name = full_name.gsub(/\A([_]+)/, '')
17
22
  next if (arg.restarg_type? || arg.kwrestarg_type?) && name.empty?
18
23
  next if allowed_names.include?(name)
19
24
 
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Naming
6
+ # This cop makes sure that rescued exceptions variables are named as
7
+ # expected.
8
+ #
9
+ # The `PreferredName` config option takes a `String`. It represents
10
+ # the required name of the variable. Its default is `e`.
11
+ #
12
+ # @example PreferredName: e (default)
13
+ # # bad
14
+ # begin
15
+ # # do something
16
+ # rescue MyException => exc
17
+ # # do something
18
+ # end
19
+ #
20
+ # # good
21
+ # begin
22
+ # # do something
23
+ # rescue MyException => e
24
+ # # do something
25
+ # end
26
+ #
27
+ # @example PreferredName: exception
28
+ # # bad
29
+ # begin
30
+ # # do something
31
+ # rescue MyException => e
32
+ # # do something
33
+ # end
34
+ #
35
+ # # good
36
+ # begin
37
+ # # do something
38
+ # rescue MyException => ex
39
+ # # do something
40
+ # end
41
+ #
42
+ class RescuedExceptionsVariableName < Cop
43
+ MSG = 'Use `%<preferred>s` instead of `%<bad>s`.'.freeze
44
+
45
+ def on_resbody(node)
46
+ exception_type, @exception_name = *node
47
+ return unless exception_type || @exception_name
48
+
49
+ @exception_name ||= exception_type.children.first
50
+ return if @exception_name.const_type? ||
51
+ variable_name == preferred_name
52
+
53
+ add_offense(node, location: location)
54
+ end
55
+
56
+ private
57
+
58
+ def preferred_name
59
+ @preferred_name ||= cop_config.fetch('PreferredName', 'e')
60
+ end
61
+
62
+ def variable_name
63
+ @variable_name ||= location.source
64
+ end
65
+
66
+ def location
67
+ @location ||= @exception_name.loc.expression
68
+ end
69
+
70
+ def message(_node = nil)
71
+ format(MSG, preferred: preferred_name, bad: variable_name)
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Checks for overriding built-in Active Record methods instead of using
7
+ # callbacks.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # class Book < ApplicationRecord
12
+ # def save
13
+ # self.title = title.upcase!
14
+ # super
15
+ # end
16
+ # end
17
+ #
18
+ # # good
19
+ # class Book < ApplicationRecord
20
+ # before_save :upcase_title
21
+ #
22
+ # def upcase_title
23
+ # self.title = title.upcase!
24
+ # end
25
+ # end
26
+ #
27
+ class ActiveRecordOverride < Cop
28
+ MSG =
29
+ 'Use %<prefer>s callbacks instead of overriding the Active Record ' \
30
+ 'method `%<bad>s`.'.freeze
31
+ BAD_METHODS = %i[create destroy save update].freeze
32
+
33
+ def on_def(node)
34
+ method_name = node.method_name
35
+ return unless BAD_METHODS.include?(node.method_name)
36
+
37
+ parent_parts = node.parent.node_parts
38
+ parent_class = parent_parts.take_while do |part|
39
+ !part.nil? && part.const_type?
40
+ end.last
41
+ return unless %w[ApplicationRecord ActiveModel::Base]
42
+ .include?(parent_class.const_name)
43
+
44
+ return unless node.descendants.any?(&:zsuper_type?)
45
+
46
+ add_offense(node, message: message(method_name))
47
+ end
48
+
49
+ private
50
+
51
+ def callback_names(method_name)
52
+ names = %w[before_ around_ after_].map do |prefix|
53
+ "`#{prefix}#{method_name}`"
54
+ end
55
+
56
+ names[-1] = "or #{names.last}"
57
+
58
+ names.join(', ')
59
+ end
60
+
61
+ def message(method_name)
62
+ format(MSG, prefer: callback_names(method_name), bad: method_name)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -6,6 +6,11 @@ module RuboCop
6
6
  # This cop checks for code that can be written with simpler conditionals
7
7
  # using `Object#blank?` defined by Active Support.
8
8
  #
9
+ # Interaction with `Style/UnlessElse`:
10
+ # The configuration of `NotPresent` will not produce an offense in the
11
+ # context of `unless else` if `Style/UnlessElse` is inabled. This is
12
+ # to prevent interference between the auto-correction of the two cops.
13
+ #
9
14
  # @example NilOrEmpty: true (default)
10
15
  # # Converts usages of `nil? || empty?` to `blank?`
11
16
  #
@@ -111,6 +116,7 @@ module RuboCop
111
116
  def on_if(node)
112
117
  return unless cop_config['UnlessPresent']
113
118
  return unless node.unless?
119
+ return if node.else? && config.for_cop('Style/UnlessElse')['Enabled']
114
120
 
115
121
  unless_present?(node) do |method_call, receiver|
116
122
  range = unless_condition(node, method_call)
@@ -6,10 +6,13 @@ module RuboCop
6
6
  # This cop checks that methods specified in the filter's `only` or
7
7
  # `except` options are defined within the same class or module.
8
8
  #
9
- # You can technically specify methods of superclass or methods added
10
- # by mixins on the filter, but these confuse developers. If you
11
- # specify methods that are defined in other classes or modules, you
12
- # should define the filter in that class or module.
9
+ # You can technically specify methods of superclass or methods added by
10
+ # mixins on the filter, but these can confuse developers. If you specify
11
+ # methods that are defined in other classes or modules, you should
12
+ # define the filter in that class or module.
13
+ #
14
+ # If you rely on behaviour defined in the superclass actions, you must
15
+ # remember to invoke `super` in the subclass actions.
13
16
  #
14
17
  # @example
15
18
  # # bad
@@ -56,6 +59,29 @@ module RuboCop
56
59
  # # something
57
60
  # end
58
61
  # end
62
+ #
63
+ # @example
64
+ # class ContentController < ApplicationController
65
+ # def update
66
+ # @content.update(content_attributes)
67
+ # end
68
+ # end
69
+ #
70
+ # class ArticlesController < ContentController
71
+ # before_action :load_article, only: [:update]
72
+ #
73
+ # # the cop requires this method, but it relies on behaviour defined
74
+ # # in the superclass, so needs to invoke `super`
75
+ # def update
76
+ # super
77
+ # end
78
+ #
79
+ # private
80
+ #
81
+ # def load_article
82
+ # @content = Article.find(params[:article_id])
83
+ # end
84
+ # end
59
85
  class LexicallyScopedActionFilter < Cop
60
86
  MSG = '%<action>s not explicitly defined on the %<type>s.'.freeze
61
87
 
@@ -22,7 +22,7 @@ module RuboCop
22
22
  PATTERN
23
23
 
24
24
  def_node_matcher :includes_noopener?, <<-PATTERN
25
- (pair {(sym :rel) (str "rel")} (str #contains_noopener?))
25
+ (pair {(sym :rel) (str "rel")} ({str sym} #contains_noopener?))
26
26
  PATTERN
27
27
 
28
28
  def_node_matcher :rel_node?, <<-PATTERN
@@ -72,17 +72,18 @@ module RuboCop
72
72
  end
73
73
 
74
74
  def add_rel(send_node, offence_node, corrector)
75
- quote_style = offence_node.children.last.source[0]
76
- new_rel_exp = ", rel: #{quote_style}noopener#{quote_style}"
75
+ opening_quote = offence_node.children.last.source[0]
76
+ closing_quote = opening_quote == ':' ? '' : opening_quote
77
+ new_rel_exp = ", rel: #{opening_quote}noopener#{closing_quote}"
77
78
  range = send_node.arguments.last.source_range
78
79
 
79
80
  corrector.insert_after(range, new_rel_exp)
80
81
  end
81
82
 
82
- def contains_noopener?(str)
83
- return false unless str
83
+ def contains_noopener?(value)
84
+ return false unless value
84
85
 
85
- str.split(' ').include?('noopener')
86
+ value.to_s.split(' ').include?('noopener')
86
87
  end
87
88
  end
88
89
  end
@@ -6,7 +6,10 @@ module RuboCop
6
6
  # This cop checks for code that can be written with simpler conditionals
7
7
  # using `Object#present?` defined by Active Support.
8
8
  #
9
- # simpler conditionals.
9
+ # Interaction with `Style/UnlessElse`:
10
+ # The configuration of `NotBlank` will not produce an offense in the
11
+ # context of `unless else` if `Style/UnlessElse` is inabled. This is
12
+ # to prevent interference between the auto-correction of the two cops.
10
13
  #
11
14
  # @example NotNilAndNotEmpty: true (default)
12
15
  # # Converts usages of `!nil? && !empty?` to `present?`
@@ -104,6 +107,7 @@ module RuboCop
104
107
  def on_if(node)
105
108
  return unless cop_config['UnlessBlank']
106
109
  return unless node.unless?
110
+ return if node.else? && config.for_cop('Style/UnlessElse')['Enabled']
107
111
 
108
112
  unless_blank?(node) do |method_call, receiver|
109
113
  range = unless_condition(node, method_call)
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # Checks Rails model validations for a redundant `allow_nil` when
7
+ # `allow_blank` is present.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # validates :x, length: { is: 5 }, allow_nil: true, allow_blank: true
12
+ #
13
+ # # bad
14
+ # validates :x, length: { is: 5 }, allow_nil: false, allow_blank: true
15
+ #
16
+ # # bad
17
+ # validates :x, length: { is: 5 }, allow_nil: false, allow_blank: false
18
+ #
19
+ # # good
20
+ # validates :x, length: { is: 5 }, allow_blank: true
21
+ #
22
+ # # good
23
+ # validates :x, length: { is: 5 }, allow_blank: false
24
+ #
25
+ # # good
26
+ # # Here, `nil` is valid but `''` is not
27
+ # validates :x, length: { is: 5 }, allow_nil: true, allow_blank: false
28
+ #
29
+ class RedundantAllowNil < Cop
30
+ include RangeHelp
31
+
32
+ MSG_SAME =
33
+ '`allow_nil` is redundant when `allow_blank` has the same value.'
34
+ .freeze
35
+ MSG_ALLOW_NIL_FALSE =
36
+ '`allow_nil: false` is redundant when `allow_blank` is true.'.freeze
37
+
38
+ def on_send(node)
39
+ return unless node.method_name == :validates
40
+
41
+ allow_nil, allow_blank = find_allow_nil_and_allow_blank(node)
42
+ allow_nil_val = allow_nil.children.last
43
+ allow_blank_val = allow_blank.children.last
44
+
45
+ if allow_nil_val.type == allow_blank_val.type
46
+ add_offense(allow_nil, message: MSG_SAME)
47
+ elsif allow_nil_val.false_type? && allow_blank_val.true_type?
48
+ add_offense(allow_nil, message: MSG_ALLOW_NIL_FALSE)
49
+ end
50
+ end
51
+
52
+ def autocorrect(node)
53
+ prv_sib = previous_sibling(node)
54
+ nxt_sib = next_sibling(node)
55
+
56
+ lambda do |corrector|
57
+ if nxt_sib
58
+ corrector.remove(range_between(node_beg(node), node_beg(nxt_sib)))
59
+ elsif prv_sib
60
+ corrector.remove(range_between(node_end(prv_sib), node_end(node)))
61
+ else
62
+ corrector.remove(node.loc.expression)
63
+ end
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ def find_allow_nil_and_allow_blank(node)
70
+ allow_nil = nil
71
+ allow_blank = nil
72
+
73
+ node.each_descendant do |descendant|
74
+ next unless descendant.pair_type?
75
+
76
+ key = descendant.children.first.value
77
+
78
+ allow_nil = descendant if key == :allow_nil
79
+ allow_blank = descendant if key == :allow_blank
80
+
81
+ break if allow_nil && allow_blank
82
+ end
83
+
84
+ [allow_nil, allow_blank]
85
+ end
86
+
87
+ def previous_sibling(node)
88
+ node.parent.children[node.sibling_index - 1]
89
+ end
90
+
91
+ def next_sibling(node)
92
+ node.parent.children[node.sibling_index + 1]
93
+ end
94
+
95
+ def node_beg(node)
96
+ node.loc.expression.begin_pos
97
+ end
98
+
99
+ def node_end(node)
100
+ node.loc.expression.end_pos
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end