rubocop 0.66.0 → 0.67.0

Sign up to get free protection for your applications and to get access to all the features.
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