rubocop 0.70.0 → 0.72.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +5 -10
- data/config/default.yml +50 -491
- data/lib/rubocop.rb +5 -53
- data/lib/rubocop/ast/builder.rb +2 -0
- data/lib/rubocop/ast/node.rb +1 -1
- data/lib/rubocop/ast/node/float_node.rb +12 -0
- data/lib/rubocop/ast/node/int_node.rb +12 -0
- data/lib/rubocop/ast/node/mixin/numeric_node.rb +21 -0
- data/lib/rubocop/ast/node/resbody_node.rb +1 -6
- data/lib/rubocop/cached_data.rb +1 -1
- data/lib/rubocop/config.rb +35 -6
- data/lib/rubocop/config_loader.rb +2 -2
- data/lib/rubocop/config_loader_resolver.rb +0 -6
- data/lib/rubocop/cop/cop.rb +0 -4
- data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +55 -0
- data/lib/rubocop/cop/layout/class_structure.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +3 -1
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +4 -0
- data/lib/rubocop/cop/layout/indent_first_argument.rb +6 -2
- data/lib/rubocop/cop/layout/indent_first_parameter.rb +7 -3
- data/lib/rubocop/cop/layout/indent_heredoc.rb +0 -1
- data/lib/rubocop/cop/layout/indentation_consistency.rb +13 -12
- data/lib/rubocop/cop/layout/indentation_width.rb +8 -4
- data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +2 -0
- data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
- data/lib/rubocop/cop/mixin/hash_alignment.rb +4 -0
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +20 -22
- data/lib/rubocop/cop/style/commented_keyword.rb +1 -1
- data/lib/rubocop/cop/style/conditional_assignment.rb +2 -1
- data/lib/rubocop/cop/style/float_division.rb +94 -0
- data/lib/rubocop/cop/style/format_string.rb +7 -3
- data/lib/rubocop/cop/style/if_inside_else.rb +42 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +7 -1
- data/lib/rubocop/cop/style/safe_navigation.rb +1 -1
- data/lib/rubocop/cop/style/ternary_parentheses.rb +12 -2
- data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +4 -0
- data/lib/rubocop/cop/style/word_array.rb +2 -2
- data/lib/rubocop/cop/style/zero_length_predicate.rb +1 -1
- data/lib/rubocop/node_pattern.rb +84 -5
- data/lib/rubocop/options.rb +0 -2
- data/lib/rubocop/processed_source.rb +5 -1
- data/lib/rubocop/rspec/cop_helper.rb +0 -1
- data/lib/rubocop/rspec/shared_contexts.rb +0 -17
- data/lib/rubocop/rspec/support.rb +0 -1
- data/lib/rubocop/runner.rb +6 -7
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop/yaml_duplication_checker.rb +8 -2
- metadata +7 -69
- data/lib/rubocop/cop/mixin/target_rails_version.rb +0 -16
- data/lib/rubocop/cop/rails/action_filter.rb +0 -117
- data/lib/rubocop/cop/rails/active_record_aliases.rb +0 -48
- data/lib/rubocop/cop/rails/active_record_override.rb +0 -82
- data/lib/rubocop/cop/rails/active_support_aliases.rb +0 -69
- data/lib/rubocop/cop/rails/application_job.rb +0 -40
- data/lib/rubocop/cop/rails/application_record.rb +0 -40
- data/lib/rubocop/cop/rails/assert_not.rb +0 -44
- data/lib/rubocop/cop/rails/belongs_to.rb +0 -102
- data/lib/rubocop/cop/rails/blank.rb +0 -164
- data/lib/rubocop/cop/rails/bulk_change_table.rb +0 -289
- data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +0 -91
- data/lib/rubocop/cop/rails/date.rb +0 -161
- data/lib/rubocop/cop/rails/delegate.rb +0 -132
- data/lib/rubocop/cop/rails/delegate_allow_blank.rb +0 -37
- data/lib/rubocop/cop/rails/dynamic_find_by.rb +0 -91
- data/lib/rubocop/cop/rails/enum_uniqueness.rb +0 -45
- data/lib/rubocop/cop/rails/environment_comparison.rb +0 -68
- data/lib/rubocop/cop/rails/exit.rb +0 -67
- data/lib/rubocop/cop/rails/file_path.rb +0 -108
- data/lib/rubocop/cop/rails/find_by.rb +0 -55
- data/lib/rubocop/cop/rails/find_each.rb +0 -51
- data/lib/rubocop/cop/rails/has_and_belongs_to_many.rb +0 -25
- data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +0 -106
- data/lib/rubocop/cop/rails/http_positional_arguments.rb +0 -117
- data/lib/rubocop/cop/rails/http_status.rb +0 -179
- data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +0 -94
- data/lib/rubocop/cop/rails/inverse_of.rb +0 -246
- data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +0 -175
- data/lib/rubocop/cop/rails/link_to_blank.rb +0 -98
- data/lib/rubocop/cop/rails/not_null_column.rb +0 -67
- data/lib/rubocop/cop/rails/output.rb +0 -49
- data/lib/rubocop/cop/rails/output_safety.rb +0 -99
- data/lib/rubocop/cop/rails/pluralization_grammar.rb +0 -107
- data/lib/rubocop/cop/rails/presence.rb +0 -124
- data/lib/rubocop/cop/rails/present.rb +0 -153
- data/lib/rubocop/cop/rails/read_write_attribute.rb +0 -74
- data/lib/rubocop/cop/rails/redundant_allow_nil.rb +0 -111
- data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +0 -136
- data/lib/rubocop/cop/rails/reflection_class_name.rb +0 -37
- data/lib/rubocop/cop/rails/refute_methods.rb +0 -76
- data/lib/rubocop/cop/rails/relative_date_constant.rb +0 -93
- data/lib/rubocop/cop/rails/request_referer.rb +0 -56
- data/lib/rubocop/cop/rails/reversible_migration.rb +0 -286
- data/lib/rubocop/cop/rails/safe_navigation.rb +0 -87
- data/lib/rubocop/cop/rails/save_bang.rb +0 -316
- data/lib/rubocop/cop/rails/scope_args.rb +0 -29
- data/lib/rubocop/cop/rails/skips_model_validations.rb +0 -87
- data/lib/rubocop/cop/rails/time_zone.rb +0 -238
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +0 -105
- data/lib/rubocop/cop/rails/unknown_env.rb +0 -63
- data/lib/rubocop/cop/rails/validation.rb +0 -109
- data/lib/rubocop/rspec/shared_examples.rb +0 -59
@@ -1,82 +0,0 @@
|
|
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`.'
|
31
|
-
BAD_METHODS = %i[create destroy save update].freeze
|
32
|
-
ACTIVE_RECORD_CLASSES = %w[ApplicationRecord ActiveModel::Base
|
33
|
-
ActiveRecord::Base].freeze
|
34
|
-
|
35
|
-
def on_def(node)
|
36
|
-
return unless BAD_METHODS.include?(node.method_name)
|
37
|
-
|
38
|
-
parent_class_name = find_parent_class_name(node)
|
39
|
-
return unless active_model?(parent_class_name)
|
40
|
-
|
41
|
-
return unless node.descendants.any?(&:zsuper_type?)
|
42
|
-
|
43
|
-
add_offense(node, message: message(node.method_name))
|
44
|
-
end
|
45
|
-
|
46
|
-
private
|
47
|
-
|
48
|
-
def active_model?(parent_class_name)
|
49
|
-
ACTIVE_RECORD_CLASSES.include?(parent_class_name)
|
50
|
-
end
|
51
|
-
|
52
|
-
def callback_names(method_name)
|
53
|
-
names = %w[before_ around_ after_].map do |prefix|
|
54
|
-
"`#{prefix}#{method_name}`"
|
55
|
-
end
|
56
|
-
|
57
|
-
names[-1] = "or #{names.last}"
|
58
|
-
|
59
|
-
names.join(', ')
|
60
|
-
end
|
61
|
-
|
62
|
-
def message(method_name)
|
63
|
-
format(MSG, prefer: callback_names(method_name), bad: method_name)
|
64
|
-
end
|
65
|
-
|
66
|
-
def find_parent_class_name(node)
|
67
|
-
return nil unless node
|
68
|
-
|
69
|
-
if node.class_type?
|
70
|
-
parent_class_name = node.node_parts[1]
|
71
|
-
|
72
|
-
return nil if parent_class_name.nil?
|
73
|
-
|
74
|
-
return parent_class_name.source
|
75
|
-
end
|
76
|
-
|
77
|
-
find_parent_class_name(node.parent)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
@@ -1,69 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Rails
|
6
|
-
# This cop checks that ActiveSupport aliases to core ruby methods
|
7
|
-
# are not used.
|
8
|
-
#
|
9
|
-
# @example
|
10
|
-
# # good
|
11
|
-
# 'some_string'.start_with?('prefix')
|
12
|
-
# 'some_string'.end_with?('suffix')
|
13
|
-
# [1, 2, 'a'] << 'b'
|
14
|
-
# [1, 2, 'a'].unshift('b')
|
15
|
-
#
|
16
|
-
# # bad
|
17
|
-
# 'some_string'.starts_with?('prefix')
|
18
|
-
# 'some_string'.ends_with?('suffix')
|
19
|
-
# [1, 2, 'a'].append('b')
|
20
|
-
# [1, 2, 'a'].prepend('b')
|
21
|
-
#
|
22
|
-
class ActiveSupportAliases < Cop
|
23
|
-
MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
|
24
|
-
|
25
|
-
ALIASES = {
|
26
|
-
starts_with?: {
|
27
|
-
original: :start_with?, matcher: '(send str :starts_with? _)'
|
28
|
-
},
|
29
|
-
ends_with?: {
|
30
|
-
original: :end_with?, matcher: '(send str :ends_with? _)'
|
31
|
-
},
|
32
|
-
append: { original: :<<, matcher: '(send array :append _)' },
|
33
|
-
prepend: { original: :unshift, matcher: '(send array :prepend _)' }
|
34
|
-
}.freeze
|
35
|
-
|
36
|
-
ALIASES.each do |aliased_method, options|
|
37
|
-
def_node_matcher aliased_method, options[:matcher]
|
38
|
-
end
|
39
|
-
|
40
|
-
def on_send(node)
|
41
|
-
ALIASES.each_key do |aliased_method|
|
42
|
-
register_offense(node, aliased_method) if
|
43
|
-
public_send(aliased_method, node)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def autocorrect(node)
|
48
|
-
return false if append(node)
|
49
|
-
|
50
|
-
lambda do |corrector|
|
51
|
-
method_name = node.loc.selector.source
|
52
|
-
replacement = ALIASES[method_name.to_sym][:original]
|
53
|
-
corrector.replace(node.loc.selector, replacement.to_s)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
private
|
58
|
-
|
59
|
-
def register_offense(node, method_name)
|
60
|
-
add_offense(
|
61
|
-
node,
|
62
|
-
message: format(MSG, prefer: ALIASES[method_name][:original],
|
63
|
-
current: method_name)
|
64
|
-
)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Rails
|
6
|
-
# This cop checks that jobs subclass ApplicationJob with Rails 5.0.
|
7
|
-
#
|
8
|
-
# @example
|
9
|
-
#
|
10
|
-
# # good
|
11
|
-
# class Rails5Job < ApplicationJob
|
12
|
-
# # ...
|
13
|
-
# end
|
14
|
-
#
|
15
|
-
# # bad
|
16
|
-
# class Rails4Job < ActiveJob::Base
|
17
|
-
# # ...
|
18
|
-
# end
|
19
|
-
class ApplicationJob < Cop
|
20
|
-
extend TargetRailsVersion
|
21
|
-
|
22
|
-
minimum_target_rails_version 5.0
|
23
|
-
|
24
|
-
MSG = 'Jobs should subclass `ApplicationJob`.'
|
25
|
-
SUPERCLASS = 'ApplicationJob'
|
26
|
-
BASE_PATTERN = '(const (const nil? :ActiveJob) :Base)'
|
27
|
-
|
28
|
-
# rubocop:disable Layout/ClassStructure
|
29
|
-
include RuboCop::Cop::EnforceSuperclass
|
30
|
-
# rubocop:enable Layout/ClassStructure
|
31
|
-
|
32
|
-
def autocorrect(node)
|
33
|
-
lambda do |corrector|
|
34
|
-
corrector.replace(node.source_range, self.class::SUPERCLASS)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Rails
|
6
|
-
# This cop checks that models subclass ApplicationRecord with Rails 5.0.
|
7
|
-
#
|
8
|
-
# @example
|
9
|
-
#
|
10
|
-
# # good
|
11
|
-
# class Rails5Model < ApplicationRecord
|
12
|
-
# # ...
|
13
|
-
# end
|
14
|
-
#
|
15
|
-
# # bad
|
16
|
-
# class Rails4Model < ActiveRecord::Base
|
17
|
-
# # ...
|
18
|
-
# end
|
19
|
-
class ApplicationRecord < Cop
|
20
|
-
extend TargetRailsVersion
|
21
|
-
|
22
|
-
minimum_target_rails_version 5.0
|
23
|
-
|
24
|
-
MSG = 'Models should subclass `ApplicationRecord`.'
|
25
|
-
SUPERCLASS = 'ApplicationRecord'
|
26
|
-
BASE_PATTERN = '(const (const nil? :ActiveRecord) :Base)'
|
27
|
-
|
28
|
-
# rubocop:disable Layout/ClassStructure
|
29
|
-
include RuboCop::Cop::EnforceSuperclass
|
30
|
-
# rubocop:enable Layout/ClassStructure
|
31
|
-
|
32
|
-
def autocorrect(node)
|
33
|
-
lambda do |corrector|
|
34
|
-
corrector.replace(node.source_range, self.class::SUPERCLASS)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
@@ -1,44 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Rails
|
6
|
-
#
|
7
|
-
# Use `assert_not` instead of `assert !`.
|
8
|
-
#
|
9
|
-
# @example
|
10
|
-
# # bad
|
11
|
-
# assert !x
|
12
|
-
#
|
13
|
-
# # good
|
14
|
-
# assert_not x
|
15
|
-
#
|
16
|
-
class AssertNot < RuboCop::Cop::Cop
|
17
|
-
MSG = 'Prefer `assert_not` over `assert !`.'
|
18
|
-
|
19
|
-
def_node_matcher :offensive?, '(send nil? :assert (send ... :!) ...)'
|
20
|
-
|
21
|
-
def on_send(node)
|
22
|
-
add_offense(node) if offensive?(node)
|
23
|
-
end
|
24
|
-
|
25
|
-
def autocorrect(node)
|
26
|
-
expression = node.loc.expression
|
27
|
-
|
28
|
-
lambda do |corrector|
|
29
|
-
corrector.replace(
|
30
|
-
expression,
|
31
|
-
corrected_source(expression.source)
|
32
|
-
)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
private
|
37
|
-
|
38
|
-
def corrected_source(source)
|
39
|
-
source.gsub(/^assert(\(| ) *! */, 'assert_not\\1')
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
@@ -1,102 +0,0 @@
|
|
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 overridden 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`.'
|
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'
|
67
|
-
|
68
|
-
def_node_matcher :match_belongs_to_with_options, <<-PATTERN
|
69
|
-
(send _ :belongs_to _
|
70
|
-
(hash <$(pair (sym :required) ${true false}) ...>)
|
71
|
-
)
|
72
|
-
PATTERN
|
73
|
-
|
74
|
-
def on_send(node)
|
75
|
-
match_belongs_to_with_options(node) do |_option_node, option_value|
|
76
|
-
message =
|
77
|
-
if option_value.true_type?
|
78
|
-
SUPERFLOUS_REQUIRE_TRUE_MSG
|
79
|
-
elsif option_value.false_type?
|
80
|
-
SUPERFLOUS_REQUIRE_FALSE_MSG
|
81
|
-
end
|
82
|
-
|
83
|
-
add_offense(node, message: message, location: :selector)
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def autocorrect(node)
|
88
|
-
option_node, option_value = match_belongs_to_with_options(node)
|
89
|
-
return unless option_node
|
90
|
-
|
91
|
-
lambda do |corrector|
|
92
|
-
if option_value.true_type?
|
93
|
-
corrector.replace(option_node.loc.expression, 'optional: false')
|
94
|
-
elsif option_value.false_type?
|
95
|
-
corrector.replace(option_node.loc.expression, 'optional: true')
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
@@ -1,164 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Rails
|
6
|
-
# This cop checks for code that can be written with simpler conditionals
|
7
|
-
# using `Object#blank?` defined by Active Support.
|
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
|
-
#
|
14
|
-
# @example NilOrEmpty: true (default)
|
15
|
-
# # Converts usages of `nil? || empty?` to `blank?`
|
16
|
-
#
|
17
|
-
# # bad
|
18
|
-
# foo.nil? || foo.empty?
|
19
|
-
# foo == nil || foo.empty?
|
20
|
-
#
|
21
|
-
# # good
|
22
|
-
# foo.blank?
|
23
|
-
#
|
24
|
-
# @example NotPresent: true (default)
|
25
|
-
# # Converts usages of `!present?` to `blank?`
|
26
|
-
#
|
27
|
-
# # bad
|
28
|
-
# !foo.present?
|
29
|
-
#
|
30
|
-
# # good
|
31
|
-
# foo.blank?
|
32
|
-
#
|
33
|
-
# @example UnlessPresent: true (default)
|
34
|
-
# # Converts usages of `unless present?` to `if blank?`
|
35
|
-
#
|
36
|
-
# # bad
|
37
|
-
# something unless foo.present?
|
38
|
-
#
|
39
|
-
# # good
|
40
|
-
# something if foo.blank?
|
41
|
-
#
|
42
|
-
# # bad
|
43
|
-
# unless foo.present?
|
44
|
-
# something
|
45
|
-
# end
|
46
|
-
#
|
47
|
-
# # good
|
48
|
-
# if foo.blank?
|
49
|
-
# something
|
50
|
-
# end
|
51
|
-
#
|
52
|
-
# # good
|
53
|
-
# def blank?
|
54
|
-
# !present?
|
55
|
-
# end
|
56
|
-
class Blank < Cop
|
57
|
-
MSG_NIL_OR_EMPTY = 'Use `%<prefer>s` instead of `%<current>s`.'
|
58
|
-
MSG_NOT_PRESENT = 'Use `%<prefer>s` instead of `%<current>s`.'
|
59
|
-
MSG_UNLESS_PRESENT = 'Use `if %<prefer>s` instead of ' \
|
60
|
-
'`%<current>s`.'
|
61
|
-
|
62
|
-
# `(send nil $_)` is not actually a valid match for an offense. Nodes
|
63
|
-
# that have a single method call on the left hand side
|
64
|
-
# (`bar || foo.empty?`) will blow up when checking
|
65
|
-
# `(send (:nil) :== $_)`.
|
66
|
-
def_node_matcher :nil_or_empty?, <<-PATTERN
|
67
|
-
(or
|
68
|
-
{
|
69
|
-
(send $_ :!)
|
70
|
-
(send $_ :nil?)
|
71
|
-
(send $_ :== nil)
|
72
|
-
(send nil :== $_)
|
73
|
-
}
|
74
|
-
{
|
75
|
-
(send $_ :empty?)
|
76
|
-
(send (send (send $_ :empty?) :!) :!)
|
77
|
-
}
|
78
|
-
)
|
79
|
-
PATTERN
|
80
|
-
|
81
|
-
def_node_matcher :not_present?, '(send (send $_ :present?) :!)'
|
82
|
-
|
83
|
-
def_node_matcher :defining_blank?, '(def :blank? (args) ...)'
|
84
|
-
|
85
|
-
def_node_matcher :unless_present?, <<-PATTERN
|
86
|
-
(:if $(send $_ :present?) {nil? (...)} ...)
|
87
|
-
PATTERN
|
88
|
-
|
89
|
-
def on_send(node)
|
90
|
-
return unless cop_config['NotPresent']
|
91
|
-
|
92
|
-
not_present?(node) do |receiver|
|
93
|
-
# accepts !present? if its in the body of a `blank?` method
|
94
|
-
next if defining_blank?(node.parent)
|
95
|
-
|
96
|
-
add_offense(node,
|
97
|
-
message: format(MSG_NOT_PRESENT,
|
98
|
-
prefer: replacement(receiver),
|
99
|
-
current: node.source))
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
def on_or(node)
|
104
|
-
return unless cop_config['NilOrEmpty']
|
105
|
-
|
106
|
-
nil_or_empty?(node) do |var1, var2|
|
107
|
-
return unless var1 == var2
|
108
|
-
|
109
|
-
add_offense(node,
|
110
|
-
message: format(MSG_NIL_OR_EMPTY,
|
111
|
-
prefer: replacement(var1),
|
112
|
-
current: node.source))
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
def on_if(node)
|
117
|
-
return unless cop_config['UnlessPresent']
|
118
|
-
return unless node.unless?
|
119
|
-
return if node.else? && config.for_cop('Style/UnlessElse')['Enabled']
|
120
|
-
|
121
|
-
unless_present?(node) do |method_call, receiver|
|
122
|
-
range = unless_condition(node, method_call)
|
123
|
-
|
124
|
-
add_offense(node,
|
125
|
-
location: range,
|
126
|
-
message: format(MSG_UNLESS_PRESENT,
|
127
|
-
prefer: replacement(receiver),
|
128
|
-
current: range.source))
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
def autocorrect(node)
|
133
|
-
lambda do |corrector|
|
134
|
-
method_call, variable1 = unless_present?(node)
|
135
|
-
|
136
|
-
if method_call
|
137
|
-
corrector.replace(node.loc.keyword, 'if')
|
138
|
-
range = method_call.loc.expression
|
139
|
-
else
|
140
|
-
variable1, _variable2 = nil_or_empty?(node) || not_present?(node)
|
141
|
-
range = node.loc.expression
|
142
|
-
end
|
143
|
-
|
144
|
-
corrector.replace(range, replacement(variable1))
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
private
|
149
|
-
|
150
|
-
def unless_condition(node, method_call)
|
151
|
-
if node.modifier_form?
|
152
|
-
node.loc.keyword.join(node.loc.expression.end)
|
153
|
-
else
|
154
|
-
node.loc.expression.begin.join(method_call.loc.expression)
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
def replacement(node)
|
159
|
-
node.respond_to?(:source) ? "#{node.source}.blank?" : 'blank?'
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|