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.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +2 -2
- data/config/default.yml +38 -12
- data/lib/rubocop.rb +5 -1
- data/lib/rubocop/ast/builder.rb +2 -0
- data/lib/rubocop/ast/node/if_node.rb +29 -0
- data/lib/rubocop/ast/node/range_node.rb +11 -0
- data/lib/rubocop/ast/traversal.rb +3 -1
- data/lib/rubocop/config.rb +2 -0
- data/lib/rubocop/cop/cop.rb +1 -1
- data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +2 -1
- data/lib/rubocop/cop/lint/disjunctive_assignment_in_constructor.rb +80 -0
- data/lib/rubocop/cop/{style → lint}/flip_flop.rb +4 -3
- data/lib/rubocop/cop/lint/shadowed_exception.rb +22 -16
- data/lib/rubocop/cop/mixin/frozen_string_literal.rb +11 -4
- data/lib/rubocop/cop/mixin/nil_methods.rb +9 -3
- data/lib/rubocop/cop/mixin/trailing_comma.rb +41 -6
- data/lib/rubocop/cop/naming/constant_name.rb +10 -1
- data/lib/rubocop/cop/naming/predicate_name.rb +1 -1
- data/lib/rubocop/cop/performance/range_include.rb +3 -0
- data/lib/rubocop/cop/rails/belongs_to.rb +120 -0
- data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +94 -0
- data/lib/rubocop/cop/rails/inverse_of.rb +4 -6
- data/lib/rubocop/cop/rails/link_to_blank.rb +37 -0
- data/lib/rubocop/cop/style/documentation_method.rb +6 -1
- data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +2 -2
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +10 -2
- data/lib/rubocop/cop/style/regexp_literal.rb +4 -3
- data/lib/rubocop/cop/style/safe_navigation.rb +10 -1
- data/lib/rubocop/cop/style/ternary_parentheses.rb +4 -6
- data/lib/rubocop/cop/style/yoda_condition.rb +58 -21
- data/lib/rubocop/options.rb +10 -5
- data/lib/rubocop/target_finder.rb +8 -30
- data/lib/rubocop/version.rb +1 -1
- 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:
|
25
|
-
#
|
26
|
-
#
|
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
|
6
|
-
#
|
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
|
-
|
20
|
+
return if heredoc?(items.last)
|
21
21
|
|
22
|
-
|
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.
|
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
|
-
#
|
42
|
-
#
|
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
|
-
#
|
66
|
-
#
|
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
|
|