rubocop 1.75.2 → 1.76.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/README.md +19 -13
- data/config/default.yml +79 -23
- data/config/obsoletion.yml +6 -3
- data/lib/rubocop/config_validator.rb +6 -6
- data/lib/rubocop/cop/autocorrect_logic.rb +18 -10
- data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -1
- data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +50 -6
- data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +1 -0
- data/lib/rubocop/cop/internal_affairs/undefined_config.rb +6 -1
- data/lib/rubocop/cop/layout/class_structure.rb +35 -0
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +6 -2
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/hash_alignment.rb +2 -2
- data/lib/rubocop/cop/layout/leading_comment_space.rb +13 -1
- data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/space_after_semicolon.rb +10 -0
- data/lib/rubocop/cop/layout/space_before_brackets.rb +6 -32
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +5 -1
- data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +3 -0
- data/lib/rubocop/cop/lint/array_literal_in_regexp.rb +2 -3
- data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -1
- data/lib/rubocop/cop/lint/circular_argument_reference.rb +2 -5
- data/lib/rubocop/cop/lint/deprecated_class_methods.rb +1 -1
- data/lib/rubocop/cop/lint/duplicate_methods.rb +84 -2
- data/lib/rubocop/cop/lint/empty_interpolation.rb +3 -1
- data/lib/rubocop/cop/lint/float_comparison.rb +27 -0
- data/lib/rubocop/cop/lint/identity_comparison.rb +19 -15
- data/lib/rubocop/cop/lint/literal_as_condition.rb +31 -25
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +1 -1
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +5 -0
- data/lib/rubocop/cop/lint/useless_assignment.rb +2 -0
- data/lib/rubocop/cop/lint/useless_default_value_argument.rb +87 -0
- data/lib/rubocop/cop/lint/useless_or.rb +98 -0
- data/lib/rubocop/cop/lint/useless_rescue.rb +1 -1
- data/lib/rubocop/cop/lint/void.rb +2 -2
- data/lib/rubocop/cop/message_annotator.rb +7 -3
- data/lib/rubocop/cop/metrics/abc_size.rb +1 -1
- data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +15 -14
- data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +2 -0
- data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
- data/lib/rubocop/cop/mixin/trailing_comma.rb +9 -5
- data/lib/rubocop/cop/naming/predicate_method.rb +216 -0
- data/lib/rubocop/cop/naming/{predicate_name.rb → predicate_prefix.rb} +2 -2
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +32 -10
- data/lib/rubocop/cop/style/arguments_forwarding.rb +8 -5
- data/lib/rubocop/cop/style/class_and_module_children.rb +19 -3
- data/lib/rubocop/cop/style/command_literal.rb +1 -1
- data/lib/rubocop/cop/style/commented_keyword.rb +2 -2
- data/lib/rubocop/cop/style/comparable_between.rb +5 -2
- data/lib/rubocop/cop/style/conditional_assignment.rb +15 -6
- data/lib/rubocop/cop/style/data_inheritance.rb +7 -0
- data/lib/rubocop/cop/style/def_with_parentheses.rb +18 -5
- data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +100 -0
- data/lib/rubocop/cop/style/eval_with_location.rb +3 -3
- data/lib/rubocop/cop/style/hash_fetch_chain.rb +0 -1
- data/lib/rubocop/cop/style/hash_transform_keys.rb +2 -2
- data/lib/rubocop/cop/style/hash_transform_values.rb +2 -2
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +3 -3
- data/lib/rubocop/cop/style/if_unless_modifier.rb +22 -4
- data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +4 -7
- data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +1 -1
- data/lib/rubocop/cop/style/it_block_parameter.rb +33 -14
- data/lib/rubocop/cop/style/lambda_call.rb +7 -2
- data/lib/rubocop/cop/style/map_into_array.rb +3 -1
- data/lib/rubocop/cop/style/map_to_hash.rb +11 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +4 -1
- data/lib/rubocop/cop/style/multiline_if_modifier.rb +2 -0
- data/lib/rubocop/cop/style/percent_q_literals.rb +1 -1
- data/lib/rubocop/cop/style/redundant_array_flatten.rb +48 -0
- data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +1 -1
- data/lib/rubocop/cop/style/redundant_format.rb +6 -1
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +0 -3
- data/lib/rubocop/cop/style/redundant_parentheses.rb +31 -3
- data/lib/rubocop/cop/style/regexp_literal.rb +1 -1
- data/lib/rubocop/cop/style/safe_navigation.rb +28 -10
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +4 -2
- data/lib/rubocop/cop/style/string_concatenation.rb +1 -2
- data/lib/rubocop/cop/style/struct_inheritance.rb +8 -1
- data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +7 -1
- data/lib/rubocop/cop/style/trailing_comma_in_array_literal.rb +1 -1
- data/lib/rubocop/cop/style/trailing_comma_in_hash_literal.rb +1 -1
- data/lib/rubocop/cop/team.rb +1 -1
- data/lib/rubocop/cop/variable_force/assignment.rb +7 -3
- data/lib/rubocop/cops_documentation_generator.rb +6 -2
- data/lib/rubocop/formatter/disabled_config_formatter.rb +2 -1
- data/lib/rubocop/formatter/html_formatter.rb +1 -1
- data/lib/rubocop/formatter/pacman_formatter.rb +1 -1
- data/lib/rubocop/rspec/cop_helper.rb +2 -2
- data/lib/rubocop/rspec/shared_contexts.rb +1 -2
- data/lib/rubocop/target_finder.rb +5 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +6 -1
- metadata +12 -7
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Lint
|
|
6
|
+
# Checks for usage of method `fetch` or `Array.new` with default value argument
|
|
7
|
+
# and block. In such cases, block will always be used as default value.
|
|
8
|
+
#
|
|
9
|
+
# This cop emulates Ruby warning "block supersedes default value argument" which
|
|
10
|
+
# applies to `Array.new`, `Array#fetch`, `Hash#fetch`, `ENV.fetch` and
|
|
11
|
+
# `Thread#fetch`.
|
|
12
|
+
#
|
|
13
|
+
# @safety
|
|
14
|
+
# This cop is unsafe because the receiver could have nonstandard implementation
|
|
15
|
+
# of `fetch`, or be a class other than the one listed above.
|
|
16
|
+
#
|
|
17
|
+
# It is also unsafe because default value argument could have side effects:
|
|
18
|
+
#
|
|
19
|
+
# [source,ruby]
|
|
20
|
+
# ----
|
|
21
|
+
# def x(a) = puts "side effect"
|
|
22
|
+
# Array.new(5, x(1)) { 2 }
|
|
23
|
+
# ----
|
|
24
|
+
#
|
|
25
|
+
# so removing it would change behavior.
|
|
26
|
+
#
|
|
27
|
+
# @example
|
|
28
|
+
# # bad
|
|
29
|
+
# x.fetch(key, default_value) { block_value }
|
|
30
|
+
# Array.new(size, default_value) { block_value }
|
|
31
|
+
#
|
|
32
|
+
# # good
|
|
33
|
+
# x.fetch(key) { block_value }
|
|
34
|
+
# Array.new(size) { block_value }
|
|
35
|
+
#
|
|
36
|
+
# # also good - in case default value argument is desired instead
|
|
37
|
+
# x.fetch(key, default_value)
|
|
38
|
+
# Array.new(size, default_value)
|
|
39
|
+
#
|
|
40
|
+
# # good - keyword arguments aren't registered as offenses
|
|
41
|
+
# x.fetch(key, keyword: :arg) { block_value }
|
|
42
|
+
#
|
|
43
|
+
# @example AllowedReceivers: ['Rails.cache']
|
|
44
|
+
# # good
|
|
45
|
+
# Rails.cache.fetch(name, options) { block }
|
|
46
|
+
#
|
|
47
|
+
class UselessDefaultValueArgument < Base
|
|
48
|
+
include AllowedReceivers
|
|
49
|
+
extend AutoCorrector
|
|
50
|
+
|
|
51
|
+
MSG = 'Block supersedes default value argument.'
|
|
52
|
+
|
|
53
|
+
RESTRICT_ON_SEND = %i[fetch new].freeze
|
|
54
|
+
|
|
55
|
+
# @!method default_value_argument_and_block(node)
|
|
56
|
+
def_node_matcher :default_value_argument_and_block, <<~PATTERN
|
|
57
|
+
(any_block
|
|
58
|
+
{
|
|
59
|
+
(call _receiver :fetch $_key $_default_value)
|
|
60
|
+
(send (const _ :Array) :new $_size $_default_value)
|
|
61
|
+
}
|
|
62
|
+
_args
|
|
63
|
+
_block_body)
|
|
64
|
+
PATTERN
|
|
65
|
+
|
|
66
|
+
def on_send(node)
|
|
67
|
+
unless (prev_arg_node, default_value_node = default_value_argument_and_block(node.parent))
|
|
68
|
+
return
|
|
69
|
+
end
|
|
70
|
+
return if allowed_receiver?(node.receiver)
|
|
71
|
+
return if hash_without_braces?(default_value_node)
|
|
72
|
+
|
|
73
|
+
add_offense(default_value_node) do |corrector|
|
|
74
|
+
corrector.remove(prev_arg_node.source_range.end.join(default_value_node.source_range))
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
alias on_csend on_send
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
def hash_without_braces?(node)
|
|
82
|
+
node.hash_type? && !node.braces?
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Lint
|
|
6
|
+
# Checks for useless OR (`||` and `or`) expressions.
|
|
7
|
+
#
|
|
8
|
+
# Some methods always return a truthy value, even when called
|
|
9
|
+
# on `nil` (e.g. `nil.to_i` evaluates to `0`). Therefore, OR expressions
|
|
10
|
+
# appended after these methods will never evaluate.
|
|
11
|
+
#
|
|
12
|
+
# @example
|
|
13
|
+
#
|
|
14
|
+
# # bad
|
|
15
|
+
# x.to_a || fallback
|
|
16
|
+
# x.to_c || fallback
|
|
17
|
+
# x.to_d || fallback
|
|
18
|
+
# x.to_i || fallback
|
|
19
|
+
# x.to_f || fallback
|
|
20
|
+
# x.to_h || fallback
|
|
21
|
+
# x.to_r || fallback
|
|
22
|
+
# x.to_s || fallback
|
|
23
|
+
# x.to_sym || fallback
|
|
24
|
+
# x.intern || fallback
|
|
25
|
+
# x.inspect || fallback
|
|
26
|
+
# x.hash || fallback
|
|
27
|
+
# x.object_id || fallback
|
|
28
|
+
# x.__id__ || fallback
|
|
29
|
+
#
|
|
30
|
+
# x.to_s or fallback
|
|
31
|
+
#
|
|
32
|
+
# # good - if fallback is same as return value of method called on nil
|
|
33
|
+
# x.to_a # nil.to_a returns []
|
|
34
|
+
# x.to_c # nil.to_c returns (0+0i)
|
|
35
|
+
# x.to_d # nil.to_d returns 0.0
|
|
36
|
+
# x.to_i # nil.to_i returns 0
|
|
37
|
+
# x.to_f # nil.to_f returns 0.0
|
|
38
|
+
# x.to_h # nil.to_h returns {}
|
|
39
|
+
# x.to_r # nil.to_r returns (0/1)
|
|
40
|
+
# x.to_s # nil.to_s returns ''
|
|
41
|
+
# x.to_sym # nil.to_sym raises an error
|
|
42
|
+
# x.intern # nil.intern raises an error
|
|
43
|
+
# x.inspect # nil.inspect returns "nil"
|
|
44
|
+
# x.hash # nil.hash returns an Integer
|
|
45
|
+
# x.object_id # nil.object_id returns an Integer
|
|
46
|
+
# x.__id__ # nil.object_id returns an Integer
|
|
47
|
+
#
|
|
48
|
+
# # good - if the intention is not to call the method on nil
|
|
49
|
+
# x&.to_a || fallback
|
|
50
|
+
# x&.to_c || fallback
|
|
51
|
+
# x&.to_d || fallback
|
|
52
|
+
# x&.to_i || fallback
|
|
53
|
+
# x&.to_f || fallback
|
|
54
|
+
# x&.to_h || fallback
|
|
55
|
+
# x&.to_r || fallback
|
|
56
|
+
# x&.to_s || fallback
|
|
57
|
+
# x&.to_sym || fallback
|
|
58
|
+
# x&.intern || fallback
|
|
59
|
+
# x&.inspect || fallback
|
|
60
|
+
# x&.hash || fallback
|
|
61
|
+
# x&.object_id || fallback
|
|
62
|
+
# x&.__id__ || fallback
|
|
63
|
+
#
|
|
64
|
+
# x&.to_s or fallback
|
|
65
|
+
#
|
|
66
|
+
class UselessOr < Base
|
|
67
|
+
MSG = '`%<rhs>s` will never evaluate because `%<lhs>s` always returns a truthy value.'
|
|
68
|
+
|
|
69
|
+
TRUTHY_RETURN_VALUE_METHODS = Set[:to_a, :to_c, :to_d, :to_i, :to_f, :to_h, :to_r,
|
|
70
|
+
:to_s, :to_sym, :intern, :inspect, :hash, :object_id,
|
|
71
|
+
:__id__].freeze
|
|
72
|
+
|
|
73
|
+
# @!method truthy_return_value_method?(node)
|
|
74
|
+
def_node_matcher :truthy_return_value_method?, <<~PATTERN
|
|
75
|
+
(send _ %TRUTHY_RETURN_VALUE_METHODS)
|
|
76
|
+
PATTERN
|
|
77
|
+
|
|
78
|
+
def on_or(node)
|
|
79
|
+
if truthy_return_value_method?(node.lhs)
|
|
80
|
+
report_offense(node, node.lhs)
|
|
81
|
+
elsif truthy_return_value_method?(node.rhs)
|
|
82
|
+
parent = node.parent
|
|
83
|
+
parent = parent.parent if parent&.begin_type?
|
|
84
|
+
|
|
85
|
+
report_offense(parent, node.rhs) if parent&.or_type?
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
def report_offense(or_node, truthy_node)
|
|
92
|
+
add_offense(or_node.loc.operator.join(or_node.rhs.source_range),
|
|
93
|
+
message: format(MSG, lhs: truthy_node.source, rhs: or_node.rhs.source))
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -128,8 +128,8 @@ module RuboCop
|
|
|
128
128
|
|
|
129
129
|
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
130
130
|
def check_void_op(node, &block)
|
|
131
|
-
node = node.children.first while node
|
|
132
|
-
return unless node
|
|
131
|
+
node = node.children.first while node&.begin_type?
|
|
132
|
+
return unless node&.call_type? && OPERATORS.include?(node.method_name)
|
|
133
133
|
if !UNARY_OPERATORS.include?(node.method_name) && node.loc.dot && node.arguments.none?
|
|
134
134
|
return
|
|
135
135
|
end
|
|
@@ -32,7 +32,7 @@ module RuboCop
|
|
|
32
32
|
# @param [String] cop_name for specific cop name
|
|
33
33
|
# @param [Hash] cop_config configs for specific cop, from config#for_cop
|
|
34
34
|
# @option cop_config [String] :StyleGuide Extension of base styleguide URL
|
|
35
|
-
# @option cop_config [String] :
|
|
35
|
+
# @option cop_config [String] :References Full reference URLs
|
|
36
36
|
# @option cop_config [String] :Details
|
|
37
37
|
#
|
|
38
38
|
# @param [Hash, nil] options optional
|
|
@@ -100,8 +100,12 @@ module RuboCop
|
|
|
100
100
|
end
|
|
101
101
|
|
|
102
102
|
def reference_urls
|
|
103
|
-
urls =
|
|
104
|
-
|
|
103
|
+
urls = cop_config
|
|
104
|
+
.values_at('References', 'Reference') # Support legacy Reference key
|
|
105
|
+
.flat_map { Array(_1) }
|
|
106
|
+
.reject(&:empty?)
|
|
107
|
+
|
|
108
|
+
urls unless urls.empty?
|
|
105
109
|
end
|
|
106
110
|
|
|
107
111
|
def extra_details?
|
|
@@ -39,7 +39,7 @@ module RuboCop
|
|
|
39
39
|
class AbcSize < Base
|
|
40
40
|
include MethodComplexity
|
|
41
41
|
|
|
42
|
-
MSG = 'Assignment Branch Condition size for
|
|
42
|
+
MSG = 'Assignment Branch Condition size for `%<method>s` is too high. ' \
|
|
43
43
|
'[%<abc_vector>s %<complexity>.4g/%<max>.4g]'
|
|
44
44
|
|
|
45
45
|
private
|
|
@@ -10,7 +10,7 @@ module RuboCop
|
|
|
10
10
|
true
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
def deltas_for_first_pair(first_pair
|
|
13
|
+
def deltas_for_first_pair(first_pair)
|
|
14
14
|
{
|
|
15
15
|
separator: separator_delta(first_pair),
|
|
16
16
|
value: value_delta(first_pair)
|
|
@@ -81,13 +81,7 @@ module RuboCop
|
|
|
81
81
|
class TableAlignment
|
|
82
82
|
include ValueAlignment
|
|
83
83
|
|
|
84
|
-
def
|
|
85
|
-
self.max_key_width = 0
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def deltas_for_first_pair(first_pair, node)
|
|
89
|
-
self.max_key_width = node.keys.map { |key| key.source.length }.max
|
|
90
|
-
|
|
84
|
+
def deltas_for_first_pair(first_pair)
|
|
91
85
|
separator_delta = separator_delta(first_pair, first_pair, 0)
|
|
92
86
|
{
|
|
93
87
|
separator: separator_delta,
|
|
@@ -97,30 +91,37 @@ module RuboCop
|
|
|
97
91
|
|
|
98
92
|
private
|
|
99
93
|
|
|
100
|
-
attr_accessor :max_key_width
|
|
101
|
-
|
|
102
94
|
def key_delta(first_pair, current_pair)
|
|
103
95
|
first_pair.key_delta(current_pair)
|
|
104
96
|
end
|
|
105
97
|
|
|
106
98
|
def hash_rocket_delta(first_pair, current_pair)
|
|
107
|
-
first_pair.loc.column + max_key_width + 1 -
|
|
99
|
+
first_pair.loc.column + max_key_width(first_pair.parent) + 1 -
|
|
100
|
+
current_pair.loc.operator.column
|
|
108
101
|
end
|
|
109
102
|
|
|
110
103
|
def value_delta(first_pair, current_pair)
|
|
111
104
|
correct_value_column = first_pair.key.loc.column +
|
|
112
|
-
|
|
113
|
-
|
|
105
|
+
max_key_width(first_pair.parent) +
|
|
106
|
+
max_delimiter_width(first_pair.parent)
|
|
114
107
|
|
|
115
108
|
current_pair.value_omission? ? 0 : correct_value_column - current_pair.value.loc.column
|
|
116
109
|
end
|
|
110
|
+
|
|
111
|
+
def max_key_width(hash_node)
|
|
112
|
+
hash_node.keys.map { |key| key.source.length }.max
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def max_delimiter_width(hash_node)
|
|
116
|
+
hash_node.pairs.map { |pair| pair.delimiter(true).length }.max
|
|
117
|
+
end
|
|
117
118
|
end
|
|
118
119
|
|
|
119
120
|
# Handles calculation of deltas when the enforced style is 'separator'.
|
|
120
121
|
class SeparatorAlignment
|
|
121
122
|
include ValueAlignment
|
|
122
123
|
|
|
123
|
-
def deltas_for_first_pair(
|
|
124
|
+
def deltas_for_first_pair(_first_pair)
|
|
124
125
|
{}
|
|
125
126
|
end
|
|
126
127
|
|
|
@@ -24,7 +24,7 @@ module RuboCop
|
|
|
24
24
|
gem_canonical_name(string_a) < gem_canonical_name(string_b)
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
-
def consecutive_lines(previous, current)
|
|
27
|
+
def consecutive_lines?(previous, current)
|
|
28
28
|
first_line = get_source_range(current, treat_comments_as_separators).first_line
|
|
29
29
|
previous.source_range.last_line == first_line - 1
|
|
30
30
|
end
|
|
@@ -107,7 +107,7 @@ module RuboCop
|
|
|
107
107
|
# of the argument is not considered multiline, even if the argument
|
|
108
108
|
# itself might span multiple lines.
|
|
109
109
|
def allowed_multiline_argument?(node)
|
|
110
|
-
elements(node).one? && !Util.begins_its_line?(node
|
|
110
|
+
elements(node).one? && !Util.begins_its_line?(node_end_location(node))
|
|
111
111
|
end
|
|
112
112
|
|
|
113
113
|
def elements(node)
|
|
@@ -127,18 +127,22 @@ module RuboCop
|
|
|
127
127
|
|
|
128
128
|
def no_elements_on_same_line?(node)
|
|
129
129
|
items = elements(node).map(&:source_range)
|
|
130
|
-
items << node
|
|
130
|
+
items << node_end_location(node)
|
|
131
131
|
items.each_cons(2).none? { |a, b| on_same_line?(a, b) }
|
|
132
132
|
end
|
|
133
133
|
|
|
134
|
+
def node_end_location(node)
|
|
135
|
+
node.loc.end || node.source_range.end.adjust(begin_pos: -1)
|
|
136
|
+
end
|
|
137
|
+
|
|
134
138
|
def on_same_line?(range1, range2)
|
|
135
139
|
range1.last_line == range2.line
|
|
136
140
|
end
|
|
137
141
|
|
|
138
142
|
def last_item_precedes_newline?(node)
|
|
139
|
-
after_last_item =
|
|
140
|
-
|
|
141
|
-
after_last_item.source
|
|
143
|
+
after_last_item = node.children.last.source_range.end.join(node.loc.end.begin)
|
|
144
|
+
|
|
145
|
+
after_last_item.source.start_with?(/,?\s*(#.*)?\n/)
|
|
142
146
|
end
|
|
143
147
|
|
|
144
148
|
def avoid_comma(kind, comma_begin_pos, extra_info)
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Naming
|
|
6
|
+
# Checks that predicate methods end with `?` and non-predicate methods do not.
|
|
7
|
+
#
|
|
8
|
+
# The names of predicate methods (methods that return a boolean value) should end
|
|
9
|
+
# in a question mark. Methods that don’t return a boolean, shouldn’t
|
|
10
|
+
# end in a question mark.
|
|
11
|
+
#
|
|
12
|
+
# The cop assesses a predicate method as one that returns boolean values. Likewise,
|
|
13
|
+
# a method that only returns literal values is assessed as non-predicate. The cop does
|
|
14
|
+
# not make an assessment if the return type is unknown (method calls, variables, etc.).
|
|
15
|
+
#
|
|
16
|
+
# NOTE: Operator methods (`def ==`, etc.) are ignored.
|
|
17
|
+
#
|
|
18
|
+
# By default, the cop runs in `conservative` mode, which allows a method to be named
|
|
19
|
+
# with a question mark as long as at least one return value is boolean. In `aggressive`
|
|
20
|
+
# mode, methods with a question mark will register an offense if any known non-boolean
|
|
21
|
+
# return values are detected.
|
|
22
|
+
#
|
|
23
|
+
# The cop also has `AllowedMethods` configuration in order to prevent the cop from
|
|
24
|
+
# registering an offense from a method name that does not confirm to the naming
|
|
25
|
+
# guidelines. By default, `call` is allowed.
|
|
26
|
+
#
|
|
27
|
+
# @example Mode: conservative (default)
|
|
28
|
+
# # bad
|
|
29
|
+
# def foo
|
|
30
|
+
# bar == baz
|
|
31
|
+
# end
|
|
32
|
+
#
|
|
33
|
+
# # good
|
|
34
|
+
# def foo?
|
|
35
|
+
# bar == baz
|
|
36
|
+
# end
|
|
37
|
+
#
|
|
38
|
+
# # bad
|
|
39
|
+
# def foo?
|
|
40
|
+
# 5
|
|
41
|
+
# end
|
|
42
|
+
#
|
|
43
|
+
# # good
|
|
44
|
+
# def foo
|
|
45
|
+
# 5
|
|
46
|
+
# end
|
|
47
|
+
#
|
|
48
|
+
# # good - operator method
|
|
49
|
+
# def ==(other)
|
|
50
|
+
# hash == other.hash
|
|
51
|
+
# end
|
|
52
|
+
#
|
|
53
|
+
# # good - at least one return value is boolean
|
|
54
|
+
# def foo?
|
|
55
|
+
# return unless bar?
|
|
56
|
+
# true
|
|
57
|
+
# end
|
|
58
|
+
#
|
|
59
|
+
# # ok - return type is not known
|
|
60
|
+
# def foo?
|
|
61
|
+
# bar
|
|
62
|
+
# end
|
|
63
|
+
#
|
|
64
|
+
# # ok - return type is not known
|
|
65
|
+
# def foo
|
|
66
|
+
# bar?
|
|
67
|
+
# end
|
|
68
|
+
#
|
|
69
|
+
# @example Mode: aggressive
|
|
70
|
+
# # bad - the method returns nil in some cases
|
|
71
|
+
# def foo?
|
|
72
|
+
# return unless bar?
|
|
73
|
+
# true
|
|
74
|
+
# end
|
|
75
|
+
#
|
|
76
|
+
class PredicateMethod < Base
|
|
77
|
+
include AllowedMethods
|
|
78
|
+
|
|
79
|
+
MSG_PREDICATE = 'Predicate method names should end with `?`.'
|
|
80
|
+
MSG_NON_PREDICATE = 'Non-predicate method names should not end with `?`.'
|
|
81
|
+
|
|
82
|
+
def on_def(node)
|
|
83
|
+
return if allowed?(node)
|
|
84
|
+
|
|
85
|
+
return_values = return_values(node.body)
|
|
86
|
+
return if acceptable?(return_values)
|
|
87
|
+
|
|
88
|
+
if node.predicate_method? && potential_non_predicate?(return_values)
|
|
89
|
+
add_offense(node.loc.name, message: MSG_NON_PREDICATE)
|
|
90
|
+
elsif !node.predicate_method? && all_return_values_boolean?(return_values)
|
|
91
|
+
add_offense(node.loc.name, message: MSG_PREDICATE)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
alias on_defs on_def
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
def allowed?(node)
|
|
99
|
+
allowed_method?(node.method_name) ||
|
|
100
|
+
node.operator_method? ||
|
|
101
|
+
node.body.nil?
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def acceptable?(return_values)
|
|
105
|
+
# In `conservative` mode, if the method returns `super`, `zsuper`, or a
|
|
106
|
+
# non-comparison method call, the method name is acceptable.
|
|
107
|
+
return false unless conservative?
|
|
108
|
+
|
|
109
|
+
return_values.any? do |value|
|
|
110
|
+
value.type?(:super, :zsuper) || non_comparison_call?(value)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def non_comparison_call?(value)
|
|
115
|
+
value.call_type? && !value.comparison_method?
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def return_values(node)
|
|
119
|
+
# Collect all the (implicit and explicit) return values of a node
|
|
120
|
+
return_values = Set.new(node.begin_type? ? [] : [extract_return_value(node)])
|
|
121
|
+
|
|
122
|
+
node.each_descendant(:return) do |return_node|
|
|
123
|
+
return_values << extract_return_value(return_node)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
last_value = last_value(node)
|
|
127
|
+
return_values << last_value if last_value
|
|
128
|
+
|
|
129
|
+
process_return_values(return_values)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def all_return_values_boolean?(return_values)
|
|
133
|
+
values = return_values.reject { |value| value.type?(:super, :zsuper) }
|
|
134
|
+
return false if values.empty?
|
|
135
|
+
|
|
136
|
+
values.all? { |value| boolean_return?(value) }
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def boolean_return?(value)
|
|
140
|
+
value.boolean_type? || (value.call_type? && value.comparison_method?)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def potential_non_predicate?(return_values)
|
|
144
|
+
# Assumes a method to be non-predicate if all return values are non-boolean literals.
|
|
145
|
+
#
|
|
146
|
+
# In `Mode: conservative`, if any of the return values is a boolean,
|
|
147
|
+
# the method name is acceptable.
|
|
148
|
+
# In `Mode: aggressive`, all return values must be booleans for a predicate
|
|
149
|
+
# method, or else an offense will be registered.
|
|
150
|
+
return false if conservative? && return_values.any? { |value| boolean_return?(value) }
|
|
151
|
+
|
|
152
|
+
return_values.any? do |value|
|
|
153
|
+
value.literal? && !value.boolean_type?
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def extract_return_value(node)
|
|
158
|
+
return node unless node.return_type?
|
|
159
|
+
|
|
160
|
+
# `return` without a value is a `nil` return.
|
|
161
|
+
return s(:nil) if node.arguments.empty?
|
|
162
|
+
|
|
163
|
+
# When there's a multiple return, it cannot be a predicate
|
|
164
|
+
# so just return an `array` sexp for simplicity.
|
|
165
|
+
return s(:array) unless node.arguments.one?
|
|
166
|
+
|
|
167
|
+
node.first_argument
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def last_value(node)
|
|
171
|
+
value = node.begin_type? ? node.children.last : node
|
|
172
|
+
value.return_type? ? extract_return_value(value) : value
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def process_return_values(return_values)
|
|
176
|
+
return_values.flat_map do |value|
|
|
177
|
+
if value.conditional?
|
|
178
|
+
process_return_values(extract_conditional_branches(value))
|
|
179
|
+
elsif and_or?(value)
|
|
180
|
+
process_return_values(extract_and_or_clauses(value))
|
|
181
|
+
else
|
|
182
|
+
value
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def and_or?(node)
|
|
188
|
+
node.type?(:and, :or)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def extract_and_or_clauses(node)
|
|
192
|
+
# Recursively traverse an `and` or `or` node to collect all clauses within
|
|
193
|
+
return node unless and_or?(node)
|
|
194
|
+
|
|
195
|
+
[extract_and_or_clauses(node.lhs), extract_and_or_clauses(node.rhs)].flatten
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def extract_conditional_branches(node)
|
|
199
|
+
return node unless node.conditional?
|
|
200
|
+
|
|
201
|
+
if node.type?(:while, :until)
|
|
202
|
+
# If there is no body, act as implicit `nil`.
|
|
203
|
+
node.body ? [last_value(node.body)] : [s(:nil)]
|
|
204
|
+
else
|
|
205
|
+
# Branches with no value act as an implicit `nil`.
|
|
206
|
+
node.branches.filter_map { |branch| branch ? last_value(branch) : s(:nil) }
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def conservative?
|
|
211
|
+
cop_config.fetch('Mode', :conservative).to_sym == :conservative
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
end
|
|
@@ -100,7 +100,7 @@ module RuboCop
|
|
|
100
100
|
# # good
|
|
101
101
|
# def_node_matcher(:even?) { |value| }
|
|
102
102
|
#
|
|
103
|
-
class
|
|
103
|
+
class PredicatePrefix < Base
|
|
104
104
|
include AllowedMethods
|
|
105
105
|
|
|
106
106
|
# @!method dynamic_method_define(node)
|
|
@@ -143,7 +143,7 @@ module RuboCop
|
|
|
143
143
|
next if predicate_prefixes.include?(forbidden_prefix)
|
|
144
144
|
|
|
145
145
|
raise ValidationError, <<~MSG.chomp
|
|
146
|
-
The `Naming/
|
|
146
|
+
The `Naming/PredicatePrefix` cop is misconfigured. Prefix #{forbidden_prefix} must be included in NamePrefix because it is included in ForbiddenPrefixes.
|
|
147
147
|
MSG
|
|
148
148
|
end
|
|
149
149
|
end
|
|
@@ -195,15 +195,27 @@ module RuboCop
|
|
|
195
195
|
def autocorrect(corrector, node)
|
|
196
196
|
case style
|
|
197
197
|
when :group
|
|
198
|
-
|
|
199
|
-
return unless def_nodes.any?
|
|
200
|
-
|
|
201
|
-
replace_defs(corrector, node, def_nodes)
|
|
198
|
+
autocorrect_group_style(corrector, node)
|
|
202
199
|
when :inline
|
|
200
|
+
autocorrect_inline_style(corrector, node)
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def autocorrect_group_style(corrector, node)
|
|
205
|
+
def_nodes = find_corresponding_def_nodes(node)
|
|
206
|
+
return unless def_nodes.any?
|
|
207
|
+
|
|
208
|
+
replace_defs(corrector, node, def_nodes)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def autocorrect_inline_style(corrector, node)
|
|
212
|
+
if node.parent&.begin_type?
|
|
213
|
+
remove_modifier_node_within_begin(corrector, node, node.parent)
|
|
214
|
+
else
|
|
203
215
|
remove_nodes(corrector, node)
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
216
|
+
end
|
|
217
|
+
select_grouped_def_nodes(node).each do |grouped_def_node|
|
|
218
|
+
insert_inline_modifier(corrector, grouped_def_node, node.method_name)
|
|
207
219
|
end
|
|
208
220
|
end
|
|
209
221
|
|
|
@@ -224,9 +236,13 @@ module RuboCop
|
|
|
224
236
|
end
|
|
225
237
|
|
|
226
238
|
def offense?(node)
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
239
|
+
if group_style?
|
|
240
|
+
return false if node.parent ? node.parent.if_type? : access_modifier_with_symbol?(node)
|
|
241
|
+
|
|
242
|
+
access_modifier_is_inlined?(node) && !right_siblings_same_inline_method?(node)
|
|
243
|
+
else
|
|
244
|
+
access_modifier_is_not_inlined?(node) && select_grouped_def_nodes(node).any?
|
|
245
|
+
end
|
|
230
246
|
end
|
|
231
247
|
|
|
232
248
|
def correctable_group_offense?(node)
|
|
@@ -331,6 +347,12 @@ module RuboCop
|
|
|
331
347
|
end
|
|
332
348
|
end
|
|
333
349
|
|
|
350
|
+
def remove_modifier_node_within_begin(corrector, modifier_node, begin_node)
|
|
351
|
+
def_node = begin_node.children[1]
|
|
352
|
+
range = modifier_node.source_range.begin.join(def_node.source_range.begin)
|
|
353
|
+
corrector.remove(range)
|
|
354
|
+
end
|
|
355
|
+
|
|
334
356
|
def def_source(node, def_nodes)
|
|
335
357
|
[
|
|
336
358
|
*processed_source.ast_with_comments[node].map(&:text),
|