rubocop 1.41.0 → 1.42.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +2 -2
- data/config/default.yml +26 -1
- data/lib/rubocop/cli.rb +1 -1
- data/lib/rubocop/config.rb +7 -7
- data/lib/rubocop/config_loader_resolver.rb +5 -1
- data/lib/rubocop/cop/base.rb +62 -61
- data/lib/rubocop/cop/cop.rb +28 -28
- data/lib/rubocop/cop/corrector.rb +23 -11
- data/lib/rubocop/cop/gemspec/dependency_version.rb +16 -18
- data/lib/rubocop/cop/layout/class_structure.rb +32 -11
- data/lib/rubocop/cop/layout/comment_indentation.rb +3 -1
- data/lib/rubocop/cop/layout/indentation_style.rb +4 -1
- data/lib/rubocop/cop/layout/line_continuation_spacing.rb +6 -6
- data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
- data/lib/rubocop/cop/layout/trailing_whitespace.rb +5 -2
- data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +19 -0
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +3 -1
- data/lib/rubocop/cop/lint/regexp_as_condition.rb +6 -0
- data/lib/rubocop/cop/lint/require_parentheses.rb +3 -1
- data/lib/rubocop/cop/lint/unused_method_argument.rb +2 -1
- data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +5 -3
- data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
- data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +4 -4
- data/lib/rubocop/cop/mixin/annotation_comment.rb +1 -1
- data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +1 -1
- data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
- data/lib/rubocop/cop/registry.rb +22 -22
- data/lib/rubocop/cop/security/compound_hash.rb +2 -1
- data/lib/rubocop/cop/style/alias.rb +9 -1
- data/lib/rubocop/cop/style/block_comments.rb +1 -1
- data/lib/rubocop/cop/style/concat_array_literals.rb +22 -2
- data/lib/rubocop/cop/style/documentation.rb +10 -4
- data/lib/rubocop/cop/style/guard_clause.rb +12 -8
- data/lib/rubocop/cop/style/hash_syntax.rb +10 -7
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +15 -0
- data/lib/rubocop/cop/style/map_to_set.rb +61 -0
- data/lib/rubocop/cop/style/method_def_parentheses.rb +11 -4
- data/lib/rubocop/cop/style/min_max_comparison.rb +73 -0
- data/lib/rubocop/cop/style/redundant_string_escape.rb +6 -3
- data/lib/rubocop/cop/style/require_order.rb +4 -2
- data/lib/rubocop/cop/style/select_by_regexp.rb +6 -2
- data/lib/rubocop/cop/style/signal_exception.rb +8 -6
- data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +4 -4
- data/lib/rubocop/cop/style/word_array.rb +41 -0
- data/lib/rubocop/cop/style/yoda_expression.rb +74 -0
- data/lib/rubocop/cop/style/zero_length_predicate.rb +31 -14
- data/lib/rubocop/cop/team.rb +29 -29
- data/lib/rubocop/cop/variable_force.rb +0 -3
- data/lib/rubocop/cops_documentation_generator.rb +11 -8
- data/lib/rubocop/formatter.rb +2 -0
- data/lib/rubocop/path_util.rb +6 -1
- data/lib/rubocop/result_cache.rb +1 -1
- data/lib/rubocop/runner.rb +10 -3
- data/lib/rubocop/target_ruby.rb +0 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +3 -0
- metadata +12 -9
@@ -256,7 +256,7 @@ module RuboCop
|
|
256
256
|
# regular dotted method calls bind more tightly than operators
|
257
257
|
# so we need to climb up the AST past them
|
258
258
|
node.each_ancestor do |ancestor|
|
259
|
-
return true if ancestor.and_type? || ancestor.or_type?
|
259
|
+
return true if ancestor.and_type? || ancestor.or_type? || ancestor.range_type?
|
260
260
|
return false unless ancestor.send_type?
|
261
261
|
return true if ancestor.operator_method?
|
262
262
|
end
|
@@ -47,7 +47,6 @@ module RuboCop
|
|
47
47
|
MSG = 'Trailing whitespace detected.'
|
48
48
|
|
49
49
|
def on_new_investigation
|
50
|
-
@heredocs = extract_heredocs(processed_source.ast)
|
51
50
|
processed_source.lines.each_with_index do |line, index|
|
52
51
|
next unless line.end_with?(' ', "\t")
|
53
52
|
|
@@ -102,10 +101,14 @@ module RuboCop
|
|
102
101
|
end
|
103
102
|
|
104
103
|
def find_heredoc(line_number)
|
105
|
-
|
104
|
+
heredocs.each { |node, r| return node if r.include?(line_number) }
|
106
105
|
nil
|
107
106
|
end
|
108
107
|
|
108
|
+
def heredocs
|
109
|
+
@heredocs ||= extract_heredocs(processed_source.ast)
|
110
|
+
end
|
111
|
+
|
109
112
|
def extract_heredocs(ast)
|
110
113
|
return [] unless ast
|
111
114
|
|
@@ -68,6 +68,12 @@ module RuboCop
|
|
68
68
|
@valid_ref = regexp_conditions.map { |condition| check_regexp(condition) }.compact.max
|
69
69
|
end
|
70
70
|
|
71
|
+
def on_in_pattern(node)
|
72
|
+
regexp_patterns = patterns(node).select(&:regexp_type?)
|
73
|
+
|
74
|
+
@valid_ref = regexp_patterns.map { |pattern| check_regexp(pattern) }.compact.max
|
75
|
+
end
|
76
|
+
|
71
77
|
def on_nth_ref(node)
|
72
78
|
backref, = *node
|
73
79
|
return if @valid_ref.nil? || backref <= @valid_ref
|
@@ -84,6 +90,19 @@ module RuboCop
|
|
84
90
|
|
85
91
|
private
|
86
92
|
|
93
|
+
def patterns(pattern_node)
|
94
|
+
pattern = pattern_node.node_parts[0]
|
95
|
+
|
96
|
+
case pattern.type
|
97
|
+
when :array_pattern, :match_alt
|
98
|
+
pattern.children
|
99
|
+
when :match_as
|
100
|
+
patterns(pattern)
|
101
|
+
else
|
102
|
+
[pattern]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
87
106
|
def check_regexp(node)
|
88
107
|
return if node.interpolation?
|
89
108
|
|
@@ -128,6 +128,7 @@ module RuboCop
|
|
128
128
|
end
|
129
129
|
end
|
130
130
|
|
131
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
131
132
|
def each_already_disabled(cop, line_ranges)
|
132
133
|
line_ranges.each_cons(2) do |previous_range, range|
|
133
134
|
next if ignore_offense?(range)
|
@@ -152,9 +153,10 @@ module RuboCop
|
|
152
153
|
cop
|
153
154
|
end
|
154
155
|
|
155
|
-
yield comment, redundant
|
156
|
+
yield comment, redundant if redundant
|
156
157
|
end
|
157
158
|
end
|
159
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
158
160
|
|
159
161
|
def find_redundant_cop(cop, range)
|
160
162
|
cop_offenses = offenses_to_check.select { |offense| offense.cop_name == cop }
|
@@ -17,13 +17,19 @@ module RuboCop
|
|
17
17
|
# do_something
|
18
18
|
# end
|
19
19
|
class RegexpAsCondition < Base
|
20
|
+
include IgnoredNode
|
20
21
|
extend AutoCorrector
|
21
22
|
|
22
23
|
MSG = 'Do not use regexp literal as a condition. ' \
|
23
24
|
'The regexp literal matches `$_` implicitly.'
|
24
25
|
|
25
26
|
def on_match_current_line(node)
|
27
|
+
return if node.ancestors.none?(&:conditional?)
|
28
|
+
return if part_of_ignored_node?(node)
|
29
|
+
|
26
30
|
add_offense(node) { |corrector| corrector.replace(node, "#{node.source} =~ $_") }
|
31
|
+
|
32
|
+
ignore_node(node)
|
27
33
|
end
|
28
34
|
end
|
29
35
|
end
|
@@ -46,7 +46,9 @@ module RuboCop
|
|
46
46
|
private
|
47
47
|
|
48
48
|
def check_ternary(ternary, node)
|
49
|
-
|
49
|
+
if node.method?(:[]) || node.assignment_method? || !ternary.condition.operator_keyword?
|
50
|
+
return
|
51
|
+
end
|
50
52
|
|
51
53
|
range = range_between(node.source_range.begin_pos, ternary.condition.source_range.end_pos)
|
52
54
|
|
@@ -100,7 +100,8 @@ module RuboCop
|
|
100
100
|
|
101
101
|
unless variable.keyword_argument?
|
102
102
|
message << " If it's necessary, use `_` or `_#{variable.name}` " \
|
103
|
-
"as an argument name to indicate that it won't be used."
|
103
|
+
"as an argument name to indicate that it won't be used. " \
|
104
|
+
"If it's unnecessary, remove it."
|
104
105
|
end
|
105
106
|
|
106
107
|
scope = variable.scope
|
@@ -77,10 +77,12 @@ module RuboCop
|
|
77
77
|
PATTERN
|
78
78
|
|
79
79
|
def on_send(node)
|
80
|
-
|
81
|
-
|
80
|
+
return unless (first_argument = node.first_argument)
|
81
|
+
|
82
|
+
if first_argument.def_type?
|
83
|
+
inspect_def(node, first_argument)
|
82
84
|
elsif node.first_argument.sym_type?
|
83
|
-
inspect_sym(node,
|
85
|
+
inspect_sym(node, first_argument)
|
84
86
|
end
|
85
87
|
end
|
86
88
|
|
@@ -25,15 +25,15 @@ module RuboCop
|
|
25
25
|
# > http://c2.com/cgi/wiki?AbcMetric
|
26
26
|
CONDITION_NODES = CyclomaticComplexity::COUNTED_NODES.freeze
|
27
27
|
|
28
|
-
def self.calculate(node, discount_repeated_attributes: false)
|
29
|
-
new(node, discount_repeated_attributes: discount_repeated_attributes).calculate
|
30
|
-
end
|
31
|
-
|
32
28
|
# TODO: move to rubocop-ast
|
33
29
|
ARGUMENT_TYPES = %i[arg optarg restarg kwarg kwoptarg kwrestarg blockarg].freeze
|
34
30
|
|
35
31
|
private_constant :BRANCH_NODES, :CONDITION_NODES, :ARGUMENT_TYPES
|
36
32
|
|
33
|
+
def self.calculate(node, discount_repeated_attributes: false)
|
34
|
+
new(node, discount_repeated_attributes: discount_repeated_attributes).calculate
|
35
|
+
end
|
36
|
+
|
37
37
|
def initialize(node)
|
38
38
|
@assignment = 0
|
39
39
|
@branch = 0
|
@@ -47,7 +47,7 @@ module RuboCop
|
|
47
47
|
match.captures
|
48
48
|
end
|
49
49
|
|
50
|
-
KEYWORDS_REGEX_CACHE = {} # rubocop:disable
|
50
|
+
KEYWORDS_REGEX_CACHE = {} # rubocop:disable Style/MutableConstant
|
51
51
|
private_constant :KEYWORDS_REGEX_CACHE
|
52
52
|
|
53
53
|
def regex
|
@@ -175,7 +175,7 @@ module RuboCop
|
|
175
175
|
|
176
176
|
def remove_optarg_equals(asgn_tokens, processed_source)
|
177
177
|
optargs = processed_source.ast.each_node(:optarg)
|
178
|
-
optarg_eql = optargs.
|
178
|
+
optarg_eql = optargs.to_set { |o| o.loc.operator.begin_pos }
|
179
179
|
asgn_tokens.reject { |t| optarg_eql.include?(t.begin_pos) }
|
180
180
|
end
|
181
181
|
end
|
@@ -108,7 +108,7 @@ module RuboCop
|
|
108
108
|
return if node.body.nil?
|
109
109
|
|
110
110
|
node.body.each_descendant(:lvar, :lvasgn).any? do |lvar|
|
111
|
-
!lvar.parent.block_pass_type? && lvar.
|
111
|
+
!lvar.parent.block_pass_type? && lvar.node_parts[0].to_s == last_argument
|
112
112
|
end
|
113
113
|
end
|
114
114
|
|
data/lib/rubocop/cop/registry.rb
CHANGED
@@ -19,6 +19,28 @@ module RuboCop
|
|
19
19
|
class Registry
|
20
20
|
include Enumerable
|
21
21
|
|
22
|
+
def self.all
|
23
|
+
global.without_department(:Test).cops
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.qualified_cop_name(name, origin)
|
27
|
+
global.qualified_cop_name(name, origin)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Changes momentarily the global registry
|
31
|
+
# Intended for testing purposes
|
32
|
+
def self.with_temporary_global(temp_global = global.dup)
|
33
|
+
previous = @global
|
34
|
+
@global = temp_global
|
35
|
+
yield
|
36
|
+
ensure
|
37
|
+
@global = previous
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.reset!
|
41
|
+
@global = new
|
42
|
+
end
|
43
|
+
|
22
44
|
attr_reader :options
|
23
45
|
|
24
46
|
def initialize(cops = [], options = {})
|
@@ -238,28 +260,6 @@ module RuboCop
|
|
238
260
|
attr_reader :global
|
239
261
|
end
|
240
262
|
|
241
|
-
def self.all
|
242
|
-
global.without_department(:Test).cops
|
243
|
-
end
|
244
|
-
|
245
|
-
def self.qualified_cop_name(name, origin)
|
246
|
-
global.qualified_cop_name(name, origin)
|
247
|
-
end
|
248
|
-
|
249
|
-
# Changes momentarily the global registry
|
250
|
-
# Intended for testing purposes
|
251
|
-
def self.with_temporary_global(temp_global = global.dup)
|
252
|
-
previous = @global
|
253
|
-
@global = temp_global
|
254
|
-
yield
|
255
|
-
ensure
|
256
|
-
@global = previous
|
257
|
-
end
|
258
|
-
|
259
|
-
def self.reset!
|
260
|
-
@global = new
|
261
|
-
end
|
262
|
-
|
263
263
|
private
|
264
264
|
|
265
265
|
def initialize_copy(reg)
|
@@ -9,7 +9,8 @@ module RuboCop
|
|
9
9
|
# Manually combining hashes is error prone and hard to follow, especially
|
10
10
|
# when there are many values. Poor implementations may also introduce
|
11
11
|
# performance or security concerns if they are prone to collisions.
|
12
|
-
# Delegating to `Array#hash` is clearer
|
12
|
+
# Delegating to `Array#hash` is clearer and safer, although it might be slower
|
13
|
+
# depending on the use case.
|
13
14
|
#
|
14
15
|
# @safety
|
15
16
|
# This cop may be unsafe if the application logic depends on the hash
|
@@ -7,6 +7,11 @@ module RuboCop
|
|
7
7
|
# depending on configuration.
|
8
8
|
# It also flags uses of `alias :symbol` rather than `alias bareword`.
|
9
9
|
#
|
10
|
+
# However, it will always enforce `method_alias` when used `alias`
|
11
|
+
# in an instance method definition and in a singleton method definition.
|
12
|
+
# If used in a block, always enforce `alias_method`
|
13
|
+
# unless it is an `instance_eval` block.
|
14
|
+
#
|
10
15
|
# @example EnforcedStyle: prefer_alias (default)
|
11
16
|
# # bad
|
12
17
|
# alias_method :bar, :foo
|
@@ -22,6 +27,7 @@ module RuboCop
|
|
22
27
|
#
|
23
28
|
# # good
|
24
29
|
# alias_method :bar, :foo
|
30
|
+
#
|
25
31
|
class Alias < Base
|
26
32
|
include ConfigurableEnforcedStyle
|
27
33
|
extend AutoCorrector
|
@@ -71,7 +77,9 @@ module RuboCop
|
|
71
77
|
end
|
72
78
|
|
73
79
|
def alias_method_possible?(node)
|
74
|
-
scope_type(node) != :instance_eval &&
|
80
|
+
scope_type(node) != :instance_eval &&
|
81
|
+
node.children.none?(&:gvar_type?) &&
|
82
|
+
node&.parent&.type != :def
|
75
83
|
end
|
76
84
|
|
77
85
|
def add_offense_for_args(node, &block)
|
@@ -32,7 +32,7 @@ module RuboCop
|
|
32
32
|
eq_begin, eq_end, contents = parts(comment)
|
33
33
|
|
34
34
|
corrector.remove(eq_begin)
|
35
|
-
unless contents.
|
35
|
+
unless contents.empty?
|
36
36
|
corrector.replace(
|
37
37
|
contents,
|
38
38
|
contents.source.gsub(/\A/, '# ').gsub(/\n\n/, "\n#\n").gsub(/\n(?=[^#])/, "\n# ")
|
@@ -30,6 +30,7 @@ module RuboCop
|
|
30
30
|
'Use `push` with elements as arguments without array brackets instead of `%<current>s`.'
|
31
31
|
RESTRICT_ON_SEND = %i[concat].freeze
|
32
32
|
|
33
|
+
# rubocop:disable Metrics
|
33
34
|
def on_send(node)
|
34
35
|
return if node.arguments.empty?
|
35
36
|
return unless node.arguments.all?(&:array_type?)
|
@@ -38,7 +39,12 @@ module RuboCop
|
|
38
39
|
current = offense.source
|
39
40
|
|
40
41
|
if node.arguments.any?(&:percent_literal?)
|
41
|
-
|
42
|
+
if percent_literals_includes_only_basic_literals?(node)
|
43
|
+
prefer = preferred_method(node)
|
44
|
+
message = format(MSG, prefer: prefer, current: current)
|
45
|
+
else
|
46
|
+
message = format(MSG_FOR_PERCENT_LITERALS, current: current)
|
47
|
+
end
|
42
48
|
else
|
43
49
|
prefer = preferred_method(node)
|
44
50
|
message = format(MSG, prefer: prefer, current: current)
|
@@ -48,6 +54,7 @@ module RuboCop
|
|
48
54
|
corrector.replace(offense, prefer)
|
49
55
|
end
|
50
56
|
end
|
57
|
+
# rubocop:enable Metrics
|
51
58
|
|
52
59
|
private
|
53
60
|
|
@@ -56,10 +63,23 @@ module RuboCop
|
|
56
63
|
end
|
57
64
|
|
58
65
|
def preferred_method(node)
|
59
|
-
new_arguments =
|
66
|
+
new_arguments =
|
67
|
+
node.arguments.map do |arg|
|
68
|
+
if arg.percent_literal?
|
69
|
+
arg.children.map(&:value).map(&:inspect)
|
70
|
+
else
|
71
|
+
arg.children.map(&:source)
|
72
|
+
end
|
73
|
+
end.join(', ')
|
60
74
|
|
61
75
|
"push(#{new_arguments})"
|
62
76
|
end
|
77
|
+
|
78
|
+
def percent_literals_includes_only_basic_literals?(node)
|
79
|
+
node.arguments.select(&:percent_literal?).all? do |arg|
|
80
|
+
arg.children.all? { |child| child.str_type? || child.sym_type? }
|
81
|
+
end
|
82
|
+
end
|
63
83
|
end
|
64
84
|
end
|
65
85
|
end
|
@@ -86,6 +86,11 @@ module RuboCop
|
|
86
86
|
(send nil? {:public_constant :private_constant} ({sym str} _))
|
87
87
|
PATTERN
|
88
88
|
|
89
|
+
# @!method include_statement?(node)
|
90
|
+
def_node_matcher :include_statement?, <<~PATTERN
|
91
|
+
(send nil? {:include :extend :prepend} const)
|
92
|
+
PATTERN
|
93
|
+
|
89
94
|
def on_class(node)
|
90
95
|
return unless node.body
|
91
96
|
|
@@ -103,7 +108,7 @@ module RuboCop
|
|
103
108
|
return if documentation_comment?(node)
|
104
109
|
return if constant_allowed?(node)
|
105
110
|
return if nodoc_self_or_outer_module?(node)
|
106
|
-
return if
|
111
|
+
return if include_statement_only?(body)
|
107
112
|
|
108
113
|
range = range_between(node.loc.expression.begin_pos, node.loc.name.end_pos)
|
109
114
|
message = format(MSG, type: node.type, identifier: identifier(node))
|
@@ -115,9 +120,10 @@ module RuboCop
|
|
115
120
|
(compact_namespace?(node) && nodoc_comment?(outer_module(node).first))
|
116
121
|
end
|
117
122
|
|
118
|
-
def
|
119
|
-
|
120
|
-
|
123
|
+
def include_statement_only?(body)
|
124
|
+
return true if include_statement?(body)
|
125
|
+
|
126
|
+
body.respond_to?(:children) && body.children.all? { |node| include_statement_only?(node) }
|
121
127
|
end
|
122
128
|
|
123
129
|
def namespace?(node)
|
@@ -189,14 +189,14 @@ module RuboCop
|
|
189
189
|
|
190
190
|
if if_branch&.send_type? && heredoc?(if_branch.last_argument)
|
191
191
|
autocorrect_heredoc_argument(corrector, node, if_branch, else_branch, guard)
|
192
|
-
elsif else_branch&.send_type? && else_branch.last_argument
|
192
|
+
elsif else_branch&.send_type? && heredoc?(else_branch.last_argument)
|
193
193
|
autocorrect_heredoc_argument(corrector, node, else_branch, if_branch, guard)
|
194
194
|
else
|
195
195
|
corrector.remove(node.loc.end)
|
196
196
|
return unless node.else?
|
197
197
|
|
198
198
|
corrector.remove(node.loc.else)
|
199
|
-
corrector.remove(
|
199
|
+
corrector.remove(range_of_branch_to_remove(node, guard))
|
200
200
|
end
|
201
201
|
end
|
202
202
|
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
@@ -206,20 +206,24 @@ module RuboCop
|
|
206
206
|
end
|
207
207
|
|
208
208
|
def autocorrect_heredoc_argument(corrector, node, heredoc_branch, leave_branch, guard)
|
209
|
+
return unless node.else?
|
210
|
+
|
209
211
|
remove_whole_lines(corrector, leave_branch.source_range)
|
210
212
|
remove_whole_lines(corrector, node.loc.else)
|
211
213
|
remove_whole_lines(corrector, node.loc.end)
|
212
|
-
remove_whole_lines(corrector,
|
214
|
+
remove_whole_lines(corrector, range_of_branch_to_remove(node, guard))
|
213
215
|
corrector.insert_after(
|
214
216
|
heredoc_branch.last_argument.loc.heredoc_end, "\n#{leave_branch.source}"
|
215
217
|
)
|
216
218
|
end
|
217
219
|
|
218
|
-
def
|
219
|
-
case guard
|
220
|
-
|
221
|
-
|
222
|
-
|
220
|
+
def range_of_branch_to_remove(node, guard)
|
221
|
+
branch = case guard
|
222
|
+
when :if then node.if_branch
|
223
|
+
when :else then node.else_branch
|
224
|
+
end
|
225
|
+
|
226
|
+
branch.source_range
|
223
227
|
end
|
224
228
|
|
225
229
|
def guard_clause_source(guard_clause)
|
@@ -28,7 +28,7 @@ module RuboCop
|
|
28
28
|
# * always - forces use of the 3.1 syntax (e.g. {foo:})
|
29
29
|
# * never - forces use of explicit hash literal value
|
30
30
|
# * either - accepts both shorthand and explicit use of hash literal value
|
31
|
-
# * consistent -
|
31
|
+
# * consistent - forces use of the 3.1 syntax only if all values can be omitted in the hash
|
32
32
|
#
|
33
33
|
# @example EnforcedStyle: ruby19 (default)
|
34
34
|
# # bad
|
@@ -92,16 +92,19 @@ module RuboCop
|
|
92
92
|
#
|
93
93
|
# @example EnforcedShorthandSyntax: consistent
|
94
94
|
#
|
95
|
-
# # bad
|
96
|
-
# {foo: , bar: bar}
|
95
|
+
# # bad - `foo` and `bar` values can be omitted
|
96
|
+
# {foo: foo, bar: bar}
|
97
97
|
#
|
98
|
-
# #
|
99
|
-
# {foo:, bar:}
|
98
|
+
# # bad - `bar` value can be omitted
|
99
|
+
# {foo:, bar: bar}
|
100
100
|
#
|
101
|
-
# # bad
|
102
|
-
# {foo
|
101
|
+
# # bad - mixed syntaxes
|
102
|
+
# {foo:, bar: baz}
|
103
103
|
#
|
104
104
|
# # good
|
105
|
+
# {foo:, bar:}
|
106
|
+
#
|
107
|
+
# # good - can't omit `baz`
|
105
108
|
# {foo: foo, bar: baz}
|
106
109
|
#
|
107
110
|
class HashSyntax < Base
|
@@ -136,6 +136,7 @@ module RuboCop
|
|
136
136
|
|
137
137
|
private
|
138
138
|
|
139
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
139
140
|
def check_branches(node, branches)
|
140
141
|
# return if any branch is empty. An empty branch can be an `if`
|
141
142
|
# without an `else` or a branch that contains only comments.
|
@@ -144,9 +145,13 @@ module RuboCop
|
|
144
145
|
tails = branches.map { |branch| tail(branch) }
|
145
146
|
check_expressions(node, tails, :after_condition) if duplicated_expressions?(node, tails)
|
146
147
|
|
148
|
+
return if last_child_of_parent?(node) &&
|
149
|
+
branches.any? { |branch| single_child_branch?(branch) }
|
150
|
+
|
147
151
|
heads = branches.map { |branch| head(branch) }
|
148
152
|
check_expressions(node, heads, :before_condition) if duplicated_expressions?(node, heads)
|
149
153
|
end
|
154
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
150
155
|
|
151
156
|
def duplicated_expressions?(node, expressions)
|
152
157
|
unique_expressions = expressions.uniq
|
@@ -180,6 +185,16 @@ module RuboCop
|
|
180
185
|
end
|
181
186
|
end
|
182
187
|
|
188
|
+
def last_child_of_parent?(node)
|
189
|
+
return true unless (parent = node.parent)
|
190
|
+
|
191
|
+
parent.child_nodes.last == node
|
192
|
+
end
|
193
|
+
|
194
|
+
def single_child_branch?(branch_node)
|
195
|
+
!branch_node.begin_type? || branch_node.children.size == 1
|
196
|
+
end
|
197
|
+
|
183
198
|
def message(node)
|
184
199
|
format(MSG, source: node.source)
|
185
200
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Looks for uses of `map.to_set` or `collect.to_set` that could be
|
7
|
+
# written with just `to_set`.
|
8
|
+
#
|
9
|
+
# @safety
|
10
|
+
# This cop is unsafe, as it can produce false positives if the receiver
|
11
|
+
# is not an `Enumerable`.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# # bad
|
15
|
+
# something.map { |i| i * 2 }.to_set
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# something.to_set { |i| i * 2 }
|
19
|
+
#
|
20
|
+
# # bad
|
21
|
+
# [1, 2, 3].collect { |i| i.to_s }.to_set
|
22
|
+
#
|
23
|
+
# # good
|
24
|
+
# [1, 2, 3].to_set { |i| i.to_s }
|
25
|
+
#
|
26
|
+
class MapToSet < Base
|
27
|
+
extend AutoCorrector
|
28
|
+
include RangeHelp
|
29
|
+
|
30
|
+
MSG = 'Pass a block to `to_set` instead of calling `%<method>s.to_set`.'
|
31
|
+
RESTRICT_ON_SEND = %i[to_set].freeze
|
32
|
+
|
33
|
+
# @!method map_to_set?(node)
|
34
|
+
def_node_matcher :map_to_set?, <<~PATTERN
|
35
|
+
$(send (block $(send _ {:map :collect}) ...) :to_set)
|
36
|
+
PATTERN
|
37
|
+
|
38
|
+
def on_send(node)
|
39
|
+
return unless (to_set_node, map_node = map_to_set?(node))
|
40
|
+
|
41
|
+
message = format(MSG, method: map_node.loc.selector.source)
|
42
|
+
add_offense(map_node.loc.selector, message: message) do |corrector|
|
43
|
+
# If the `to_set` call already has a block, do not autocorrect.
|
44
|
+
next if to_set_node.block_node
|
45
|
+
|
46
|
+
autocorrect(corrector, to_set_node, map_node)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def autocorrect(corrector, to_set, map)
|
53
|
+
removal_range = range_between(to_set.loc.dot.begin_pos, to_set.loc.selector.end_pos)
|
54
|
+
|
55
|
+
corrector.remove(range_with_surrounding_space(removal_range, side: :left))
|
56
|
+
corrector.replace(map.loc.selector, 'to_set')
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -10,7 +10,9 @@ module RuboCop
|
|
10
10
|
#
|
11
11
|
# 1. Endless methods
|
12
12
|
# 2. Argument lists containing a `forward-arg` (`...`)
|
13
|
-
# 3. Argument lists containing an anonymous
|
13
|
+
# 3. Argument lists containing an anonymous rest arguments forwarding (`*`)
|
14
|
+
# 4. Argument lists containing an anonymous keyword rest arguments forwarding (`**`)
|
15
|
+
# 5. Argument lists containing an anonymous block forwarding (`&`)
|
14
16
|
#
|
15
17
|
# Removing the parens would be a syntax error here.
|
16
18
|
#
|
@@ -130,9 +132,11 @@ module RuboCop
|
|
130
132
|
# Regardless of style, parentheses are necessary for:
|
131
133
|
# 1. Endless methods
|
132
134
|
# 2. Argument lists containing a `forward-arg` (`...`)
|
133
|
-
# 3. Argument lists containing an anonymous
|
135
|
+
# 3. Argument lists containing an anonymous rest arguments forwarding (`*`)
|
136
|
+
# 4. Argument lists containing an anonymous keyword rest arguments forwarding (`**`)
|
137
|
+
# 5. Argument lists containing an anonymous block forwarding (`&`)
|
134
138
|
# Removing the parens would be a syntax error here.
|
135
|
-
node.endless? ||
|
139
|
+
node.endless? || anonymous_arguments?(node)
|
136
140
|
end
|
137
141
|
|
138
142
|
def require_parentheses?(args)
|
@@ -162,7 +166,10 @@ module RuboCop
|
|
162
166
|
end
|
163
167
|
end
|
164
168
|
|
165
|
-
def
|
169
|
+
def anonymous_arguments?(node)
|
170
|
+
return true if node.arguments.any? do |arg|
|
171
|
+
arg.forward_arg_type? || arg.restarg_type? || arg.kwrestarg_type?
|
172
|
+
end
|
166
173
|
return false unless (last_argument = node.arguments.last)
|
167
174
|
|
168
175
|
last_argument.blockarg_type? && last_argument.name.nil?
|