rubocop 0.62.0 → 0.63.0

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