rubocop 0.62.0 → 0.63.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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +2 -2
  4. data/config/default.yml +38 -12
  5. data/lib/rubocop.rb +5 -1
  6. data/lib/rubocop/ast/builder.rb +2 -0
  7. data/lib/rubocop/ast/node/if_node.rb +29 -0
  8. data/lib/rubocop/ast/node/range_node.rb +11 -0
  9. data/lib/rubocop/ast/traversal.rb +3 -1
  10. data/lib/rubocop/config.rb +2 -0
  11. data/lib/rubocop/cop/cop.rb +1 -1
  12. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +2 -1
  13. data/lib/rubocop/cop/lint/disjunctive_assignment_in_constructor.rb +80 -0
  14. data/lib/rubocop/cop/{style → lint}/flip_flop.rb +4 -3
  15. data/lib/rubocop/cop/lint/shadowed_exception.rb +22 -16
  16. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +11 -4
  17. data/lib/rubocop/cop/mixin/nil_methods.rb +9 -3
  18. data/lib/rubocop/cop/mixin/trailing_comma.rb +41 -6
  19. data/lib/rubocop/cop/naming/constant_name.rb +10 -1
  20. data/lib/rubocop/cop/naming/predicate_name.rb +1 -1
  21. data/lib/rubocop/cop/performance/range_include.rb +3 -0
  22. data/lib/rubocop/cop/rails/belongs_to.rb +120 -0
  23. data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +94 -0
  24. data/lib/rubocop/cop/rails/inverse_of.rb +4 -6
  25. data/lib/rubocop/cop/rails/link_to_blank.rb +37 -0
  26. data/lib/rubocop/cop/style/documentation_method.rb +6 -1
  27. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +2 -2
  28. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +10 -2
  29. data/lib/rubocop/cop/style/regexp_literal.rb +4 -3
  30. data/lib/rubocop/cop/style/safe_navigation.rb +10 -1
  31. data/lib/rubocop/cop/style/ternary_parentheses.rb +4 -6
  32. data/lib/rubocop/cop/style/yoda_condition.rb +58 -21
  33. data/lib/rubocop/options.rb +10 -5
  34. data/lib/rubocop/target_finder.rb +8 -30
  35. data/lib/rubocop/version.rb +1 -1
  36. metadata +7 -3
@@ -21,11 +21,18 @@ module RuboCop
21
21
  def frozen_string_literals_enabled?
22
22
  ruby_version = processed_source.ruby_version
23
23
  return false unless ruby_version
24
- # TODO: Whether frozen string literals will be the default in Ruby 3.0
25
- # or not is still unclear as of February 2018.
26
- # It may be necessary to change this code in the future.
24
+ # TODO: Ruby officially abandon making frozen string literals default
25
+ # for Ruby 3.0.
26
+ # https://bugs.ruby-lang.org/issues/11473#note-53
27
+ # Whether frozen string literals will be the default after Ruby 3.0
28
+ # or not is still unclear as of January 2019.
29
+ # It may be necessary to add this code in the future.
30
+ #
31
+ # return true if ruby_version >= 3.1
32
+ #
33
+ # And the above `ruby_version >= 3.1` is undecidedd whether it will be
34
+ # Ruby 3.1, 3.2, 4.0 or others.
27
35
  # See https://bugs.ruby-lang.org/issues/8976#note-41 for details.
28
- return true if ruby_version >= 3.0
29
36
  return false unless ruby_version >= 2.3
30
37
 
31
38
  leading_comment_lines.any? do |line|
@@ -2,13 +2,19 @@
2
2
 
3
3
  module RuboCop
4
4
  module Cop
5
- # This module provides a list of methods that are either in the NilClass,
6
- # or in the cop's configuration parameter Whitelist.
5
+ # This module provides a list of methods that are:
6
+ # 1. In the NilClass by default
7
+ # 2. Added to NilClass by explicitly requiring any standard libraries
8
+ # 3. Cop's configuration parameter Whitelist.
7
9
  module NilMethods
8
10
  private
9
11
 
10
12
  def nil_methods
11
- nil.methods + whitelist
13
+ nil.methods + other_stdlib_methods + whitelist
14
+ end
15
+
16
+ def other_stdlib_methods
17
+ [:to_d]
12
18
  end
13
19
 
14
20
  def whitelist
@@ -17,9 +17,9 @@ module RuboCop
17
17
  end
18
18
 
19
19
  def check(node, items, kind, begin_pos, end_pos)
20
- after_last_item = range_between(begin_pos, end_pos)
20
+ return if heredoc?(items.last)
21
21
 
22
- return if heredoc?(after_last_item.source)
22
+ after_last_item = range_between(begin_pos, end_pos)
23
23
 
24
24
  comma_offset = after_last_item.source =~ /,/
25
25
 
@@ -76,10 +76,6 @@ module RuboCop
76
76
  end
77
77
  end
78
78
 
79
- def heredoc?(source_after_last_item)
80
- source_after_last_item !~ /^\s*#/ && source_after_last_item =~ /\w/
81
- end
82
-
83
79
  # Returns true if the node has round/square/curly brackets.
84
80
  def brackets?(node)
85
81
  node.loc.end
@@ -170,6 +166,45 @@ module RuboCop
170
166
  def avoid_autocorrect?(_nodes)
171
167
  false
172
168
  end
169
+
170
+ def heredoc?(node)
171
+ return false unless node.is_a?(RuboCop::AST::Node)
172
+ return true if node.loc.respond_to?(:heredoc_body)
173
+
174
+ return heredoc_send?(node) if node.send_type?
175
+
176
+ # handle hash values
177
+ #
178
+ # some_method({
179
+ # 'auth' => <<-SOURCE
180
+ # ...
181
+ # SOURCE
182
+ # })
183
+ if node.pair_type? || node.hash_type?
184
+ return heredoc?(node.children.last)
185
+ end
186
+
187
+ false
188
+ end
189
+
190
+ def heredoc_send?(node)
191
+ # handle heredocs with methods
192
+ #
193
+ # some_method(<<-CODE.strip.chomp)
194
+ # ...
195
+ # CODE
196
+ return heredoc?(node.children.first) if node.children.size == 2
197
+ # handle nested methods
198
+ #
199
+ # some_method(
200
+ # another_method(<<-CODE.strip.chomp)
201
+ # ...
202
+ # CODE
203
+ # )
204
+ return heredoc?(node.children.last) if node.children.size > 2
205
+
206
+ false
207
+ end
173
208
  end
174
209
  end
175
210
  end
@@ -54,13 +54,22 @@ module RuboCop
54
54
  def allowed_assignment?(value)
55
55
  value && %i[block const casgn].include?(value.type) ||
56
56
  allowed_method_call_on_rhs?(value) ||
57
- class_or_struct_return_method?(value)
57
+ class_or_struct_return_method?(value) ||
58
+ allowed_conditional_expression_on_rhs?(value)
58
59
  end
59
60
 
60
61
  def allowed_method_call_on_rhs?(node)
61
62
  node && node.send_type? &&
62
63
  (node.receiver.nil? || !node.receiver.literal?)
63
64
  end
65
+
66
+ def allowed_conditional_expression_on_rhs?(node)
67
+ node && node.if_type? && contains_contant?(node)
68
+ end
69
+
70
+ def contains_contant?(node)
71
+ node.branches.any?(&:const_type?)
72
+ end
64
73
  end
65
74
  end
66
75
  end
@@ -61,7 +61,7 @@ module RuboCop
61
61
  private
62
62
 
63
63
  def allowed_method_name?(method_name, prefix)
64
- !method_name.start_with?(prefix) ||
64
+ !method_name.match(/^#{prefix}[^0-9]/) ||
65
65
  method_name == expected_name(method_name, prefix) ||
66
66
  method_name.end_with?('=') ||
67
67
  predicate_whitelist.include?(method_name)
@@ -9,6 +9,9 @@ module RuboCop
9
9
  # end points of the `Range`. In a great majority of cases, this is what
10
10
  # is wanted.
11
11
  #
12
+ # This cop is `Safe: false` by default because `Range#include?` and
13
+ # `Range#cover?` are not equivalent behaviour.
14
+ #
12
15
  # @example
13
16
  # # bad
14
17
  # ('a'..'z').include?('b') # => true
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop looks for belongs_to associations where we control whether the
7
+ # association is required via the deprecated `required` option instead.
8
+ #
9
+ # Since Rails 5, belongs_to associations are required by default and this
10
+ # can be controlled through the use of `optional: true`.
11
+ #
12
+ # From the release notes:
13
+ #
14
+ # belongs_to will now trigger a validation error by default if the
15
+ # association is not present. You can turn this off on a
16
+ # per-association basis with optional: true. Also deprecate required
17
+ # option in favor of optional for belongs_to. (Pull Request)
18
+ #
19
+ # In the case that the developer is doing `required: false`, we
20
+ # definitely want to autocorrect to `optional: true`.
21
+ #
22
+ # However, without knowing whether they've set overriden the default
23
+ # value of `config.active_record.belongs_to_required_by_default`, we
24
+ # can't say whether it's safe to remove `required: true` or whether we
25
+ # should replace it with `optional: false` (or, similarly, remove a
26
+ # superfluous `optional: false`). Therefore, in the cases we're using
27
+ # `required: true`, we'll simply invert it to `optional: false` and the
28
+ # user can remove depending on their defaults.
29
+ #
30
+ # @example
31
+ # # bad
32
+ # class Post < ApplicationRecord
33
+ # belongs_to :blog, required: false
34
+ # end
35
+ #
36
+ # # good
37
+ # class Post < ApplicationRecord
38
+ # belongs_to :blog, optional: true
39
+ # end
40
+ #
41
+ # # bad
42
+ # class Post < ApplicationRecord
43
+ # belongs_to :blog, required: true
44
+ # end
45
+ #
46
+ # # good
47
+ # class Post < ApplicationRecord
48
+ # belongs_to :blog, optional: false
49
+ # end
50
+ #
51
+ # @see https://guides.rubyonrails.org/5_0_release_notes.html
52
+ # @see https://github.com/rails/rails/pull/18937
53
+ class BelongsTo < Cop
54
+ extend TargetRailsVersion
55
+
56
+ minimum_target_rails_version 5.0
57
+
58
+ SUPERFLOUS_REQUIRE_FALSE_MSG =
59
+ 'You specified `required: false`, in Rails > 5.0 the required ' \
60
+ 'option is deprecated and you want to use `optional: true`.'.freeze
61
+
62
+ SUPERFLOUS_REQUIRE_TRUE_MSG =
63
+ 'You specified `required: true`, in Rails > 5.0 the required ' \
64
+ 'option is deprecated and you want to use `optional: false`. ' \
65
+ 'In most configurations, this is the default and you can omit ' \
66
+ 'this option altogether'.freeze
67
+
68
+ def_node_matcher :match_belongs_to_with_options, <<-PATTERN
69
+ (send $_ :belongs_to _ (hash $...))
70
+ PATTERN
71
+
72
+ def_node_matcher :match_required_false?, <<-PATTERN
73
+ (pair (sym :required) false)
74
+ PATTERN
75
+
76
+ def_node_matcher :match_required_true?, <<-PATTERN
77
+ (pair (sym :required) true)
78
+ PATTERN
79
+
80
+ def on_send(node)
81
+ opt = extract_required_option(node)
82
+ return unless opt
83
+ return unless match_required_true?(opt) || match_required_false?(opt)
84
+
85
+ message =
86
+ if match_required_true?(opt)
87
+ SUPERFLOUS_REQUIRE_TRUE_MSG
88
+ elsif match_required_false?(opt)
89
+ SUPERFLOUS_REQUIRE_FALSE_MSG
90
+ end
91
+
92
+ add_offense(node, message: message, location: :selector)
93
+ end
94
+
95
+ def autocorrect(node)
96
+ opt = extract_required_option(node)
97
+ return unless opt
98
+
99
+ lambda do |corrector|
100
+ if match_required_true?(opt)
101
+ corrector.replace(opt.loc.expression, 'optional: false')
102
+ elsif match_required_false?(opt)
103
+ corrector.replace(opt.loc.expression, 'optional: true')
104
+ end
105
+ end
106
+ end
107
+
108
+ def extract_required_option(node)
109
+ _, opts = match_belongs_to_with_options(node)
110
+ return unless opts
111
+
112
+ opts.find do |opt|
113
+ match_required_true?(opt) ||
114
+ match_required_false?(opt)
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop checks that `if` and `only` (or `except`) are not used together
7
+ # as options of `skip_*` action filter.
8
+ #
9
+ # The `if` option will be ignored when `if` and `only` are used together.
10
+ # Similarly, the `except` option will be ignored when `if` and `except`
11
+ # are used together.
12
+ #
13
+ # @example
14
+ # # bad
15
+ # class MyPageController < ApplicationController
16
+ # skip_before_action :login_required,
17
+ # only: :show, if: :trusted_origin?
18
+ # end
19
+ #
20
+ # # good
21
+ # class MyPageController < ApplicationController
22
+ # skip_before_action :login_required,
23
+ # if: -> { trusted_origin? && action_name == "show" }
24
+ # end
25
+ #
26
+ # @example
27
+ # # bad
28
+ # class MyPageController < ApplicationController
29
+ # skip_before_action :login_required,
30
+ # except: :admin, if: :trusted_origin?
31
+ # end
32
+ #
33
+ # # good
34
+ # class MyPageController < ApplicationController
35
+ # skip_before_action :login_required,
36
+ # if: -> { trusted_origin? && action_name != "admin" }
37
+ # end
38
+ #
39
+ # @see https://api.rubyonrails.org/classes/AbstractController/Callbacks/ClassMethods.html#method-i-_normalize_callback_options
40
+ class IgnoredSkipActionFilterOption < Cop
41
+ MSG = <<-MSG.strip_indent.chomp.freeze
42
+ `%<ignore>s` option will be ignored when `%<prefer>s` and `%<ignore>s` are used together.
43
+ MSG
44
+
45
+ FILTERS = %w[
46
+ :skip_after_action
47
+ :skip_around_action
48
+ :skip_before_action
49
+ :skip_action_callback
50
+ ].freeze
51
+
52
+ def_node_matcher :filter_options, <<-PATTERN
53
+ (send
54
+ nil?
55
+ {#{FILTERS.join(' ')}}
56
+ _
57
+ $_)
58
+ PATTERN
59
+
60
+ def on_send(node)
61
+ options = filter_options(node)
62
+ return unless options
63
+ return unless options.hash_type?
64
+
65
+ options = options_hash(options)
66
+
67
+ if if_and_only?(options)
68
+ add_offense(options[:if],
69
+ message: format(MSG, prefer: :only, ignore: :if))
70
+ elsif if_and_except?(options)
71
+ add_offense(options[:except],
72
+ message: format(MSG, prefer: :if, ignore: :except))
73
+ end
74
+ end
75
+
76
+ private
77
+
78
+ def options_hash(options)
79
+ options.pairs
80
+ .select { |pair| pair.key.sym_type? }
81
+ .map { |pair| [pair.key.value, pair] }.to_h
82
+ end
83
+
84
+ def if_and_only?(options)
85
+ options.key?(:if) && options.key?(:only)
86
+ end
87
+
88
+ def if_and_except?(options)
89
+ options.key?(:if) && options.key?(:except)
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -38,9 +38,8 @@ module RuboCop
38
38
  # # good
39
39
  # class Blog < ApplicationRecord
40
40
  # has_many(:posts,
41
- # -> { order(published_at: :desc) },
42
- # inverse_of: :blog
43
- # )
41
+ # -> { order(published_at: :desc) },
42
+ # inverse_of: :blog)
44
43
  # end
45
44
  #
46
45
  # class Post < ApplicationRecord
@@ -62,9 +61,8 @@ module RuboCop
62
61
  # # When you don't want to use the inverse association.
63
62
  # class Blog < ApplicationRecord
64
63
  # has_many(:posts,
65
- # -> { order(published_at: :desc) },
66
- # inverse_of: false
67
- # )
64
+ # -> { order(published_at: :desc) },
65
+ # inverse_of: false)
68
66
  # end
69
67
  #
70
68
  # @example
@@ -25,6 +25,10 @@ module RuboCop
25
25
  (pair {(sym :rel) (str "rel")} (str #contains_noopener?))
26
26
  PATTERN
27
27
 
28
+ def_node_matcher :rel_node?, <<-PATTERN
29
+ (pair {(sym :rel) (str "rel")} (str _))
30
+ PATTERN
31
+
28
32
  def on_send(node)
29
33
  return unless node.method?(:link_to)
30
34
 
@@ -38,8 +42,41 @@ module RuboCop
38
42
  end
39
43
  end
40
44
 
45
+ def autocorrect(node)
46
+ lambda do |corrector|
47
+ send_node = node.parent.parent
48
+
49
+ option_nodes = send_node.each_child_node(:hash)
50
+ rel_node = nil
51
+ option_nodes.map(&:children).each do |options|
52
+ rel_node ||= options.find { |o| rel_node?(o) }
53
+ end
54
+
55
+ if rel_node
56
+ append_to_rel(rel_node, corrector)
57
+ else
58
+ add_rel(send_node, node, corrector)
59
+ end
60
+ end
61
+ end
62
+
41
63
  private
42
64
 
65
+ def append_to_rel(rel_node, corrector)
66
+ existing_rel = rel_node.children.last.value
67
+ str_range = rel_node.children.last.loc.expression.adjust(
68
+ begin_pos: 1,
69
+ end_pos: -1
70
+ )
71
+ corrector.replace(str_range, "#{existing_rel} noopener")
72
+ end
73
+
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}"
77
+ corrector.insert_after(send_node.loc.expression, new_rel_exp)
78
+ end
79
+
43
80
  def contains_noopener?(str)
44
81
  return false unless str
45
82