rubocop 1.1.0 → 1.2.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 +14 -3
- data/config/default.yml +31 -5
- data/exe/rubocop +1 -1
- data/lib/rubocop.rb +4 -0
- data/lib/rubocop/cop/layout/else_alignment.rb +15 -2
- data/lib/rubocop/cop/layout/end_alignment.rb +3 -3
- data/lib/rubocop/cop/layout/hash_alignment.rb +4 -4
- data/lib/rubocop/cop/layout/space_around_block_parameters.rb +24 -18
- data/lib/rubocop/cop/layout/space_inside_parens.rb +35 -13
- data/lib/rubocop/cop/lint/else_layout.rb +29 -3
- data/lib/rubocop/cop/lint/empty_block.rb +15 -2
- data/lib/rubocop/cop/lint/loop.rb +0 -4
- data/lib/rubocop/cop/lint/nested_percent_literal.rb +14 -0
- data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +58 -0
- data/lib/rubocop/cop/lint/useless_setter_call.rb +6 -1
- data/lib/rubocop/cop/mixin/configurable_numbering.rb +3 -3
- data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +11 -1
- data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +11 -5
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +67 -18
- data/lib/rubocop/cop/naming/variable_number.rb +82 -8
- data/lib/rubocop/cop/style/bisected_attr_accessor.rb +0 -4
- data/lib/rubocop/cop/style/case_like_if.rb +0 -4
- data/lib/rubocop/cop/style/collection_compact.rb +85 -0
- data/lib/rubocop/cop/style/double_negation.rb +6 -1
- data/lib/rubocop/cop/style/hash_syntax.rb +3 -3
- data/lib/rubocop/cop/style/keyword_parameters_order.rb +12 -0
- data/lib/rubocop/cop/style/mixin_grouping.rb +0 -4
- data/lib/rubocop/cop/style/negated_if_else_condition.rb +99 -0
- data/lib/rubocop/cop/style/raise_args.rb +21 -6
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +1 -1
- data/lib/rubocop/cop/util.rb +4 -0
- data/lib/rubocop/ext/regexp_node.rb +10 -5
- data/lib/rubocop/ext/regexp_parser.rb +9 -2
- data/lib/rubocop/formatter/formatter_set.rb +1 -0
- data/lib/rubocop/formatter/git_hub_actions_formatter.rb +47 -0
- data/lib/rubocop/options.rb +2 -0
- data/lib/rubocop/version.rb +1 -1
- metadata +7 -3
@@ -227,10 +227,6 @@ module RuboCop
|
|
227
227
|
range_between(node.parent.loc.keyword.begin_pos, node.loc.expression.end_pos)
|
228
228
|
end
|
229
229
|
|
230
|
-
def indent(node)
|
231
|
-
' ' * node.loc.column
|
232
|
-
end
|
233
|
-
|
234
230
|
# Named captures work with `=~` (if regexp is on lhs) and with `match` (both sides)
|
235
231
|
def regexp_with_working_captures?(node)
|
236
232
|
case node.type
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# This cop checks for places where custom logic on rejection nils from arrays
|
7
|
+
# and hashes can be replaced with `{Array,Hash}#{compact,compact!}`.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# array.reject { |e| e.nil? }
|
12
|
+
# array.select { |e| !e.nil? }
|
13
|
+
#
|
14
|
+
# # good
|
15
|
+
# array.compact
|
16
|
+
#
|
17
|
+
# # bad
|
18
|
+
# hash.reject! { |k, v| v.nil? }
|
19
|
+
# hash.select! { |k, v| !v.nil? }
|
20
|
+
#
|
21
|
+
# # good
|
22
|
+
# hash.compact!
|
23
|
+
#
|
24
|
+
class CollectionCompact < Base
|
25
|
+
include RangeHelp
|
26
|
+
extend AutoCorrector
|
27
|
+
|
28
|
+
MSG = 'Use `%<good>s` instead of `%<bad>s`.'
|
29
|
+
|
30
|
+
RESTRICT_ON_SEND = %i[reject reject! select select!].freeze
|
31
|
+
|
32
|
+
def_node_matcher :reject_method?, <<~PATTERN
|
33
|
+
(block
|
34
|
+
(send
|
35
|
+
_ ${:reject :reject!})
|
36
|
+
$(args ...)
|
37
|
+
(send
|
38
|
+
$(lvar _) :nil?))
|
39
|
+
PATTERN
|
40
|
+
|
41
|
+
def_node_matcher :select_method?, <<~PATTERN
|
42
|
+
(block
|
43
|
+
(send
|
44
|
+
_ ${:select :select!})
|
45
|
+
$(args ...)
|
46
|
+
(send
|
47
|
+
(send
|
48
|
+
$(lvar _) :nil?) :!))
|
49
|
+
PATTERN
|
50
|
+
|
51
|
+
def on_send(node)
|
52
|
+
block_node = node.parent
|
53
|
+
return unless block_node&.block_type?
|
54
|
+
|
55
|
+
return unless (method_name, args, receiver =
|
56
|
+
reject_method?(block_node) || select_method?(block_node))
|
57
|
+
|
58
|
+
return unless args.last.source == receiver.source
|
59
|
+
|
60
|
+
range = offense_range(node, block_node)
|
61
|
+
good = good_method_name(method_name)
|
62
|
+
message = format(MSG, good: good, bad: range.source)
|
63
|
+
|
64
|
+
add_offense(range, message: message) do |corrector|
|
65
|
+
corrector.replace(range, good)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def good_method_name(method_name)
|
72
|
+
if method_name.to_s.end_with?('!')
|
73
|
+
'compact!'
|
74
|
+
else
|
75
|
+
'compact'
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def offense_range(send_node, block_node)
|
80
|
+
range_between(send_node.loc.selector.begin_pos, block_node.loc.end.end_pos)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -34,6 +34,7 @@ module RuboCop
|
|
34
34
|
# this is rarely a problem in practice.
|
35
35
|
class DoubleNegation < Base
|
36
36
|
include ConfigurableEnforcedStyle
|
37
|
+
extend AutoCorrector
|
37
38
|
|
38
39
|
MSG = 'Avoid the use of double negation (`!!`).'
|
39
40
|
RESTRICT_ON_SEND = %i[!].freeze
|
@@ -44,7 +45,11 @@ module RuboCop
|
|
44
45
|
return unless double_negative?(node) && node.prefix_bang?
|
45
46
|
return if style == :allowed_in_returns && allowed_in_returns?(node)
|
46
47
|
|
47
|
-
|
48
|
+
location = node.loc.selector
|
49
|
+
add_offense(location) do |corrector|
|
50
|
+
corrector.remove(location)
|
51
|
+
corrector.insert_after(node, '.nil?')
|
52
|
+
end
|
48
53
|
end
|
49
54
|
|
50
55
|
private
|
@@ -97,10 +97,10 @@ module RuboCop
|
|
97
97
|
end
|
98
98
|
|
99
99
|
def no_mixed_keys_check(pairs)
|
100
|
-
if
|
101
|
-
check(pairs, ':', MSG_NO_MIXED_KEYS)
|
102
|
-
else
|
100
|
+
if sym_indices?(pairs)
|
103
101
|
check(pairs, pairs.first.inverse_delimiter, MSG_NO_MIXED_KEYS)
|
102
|
+
else
|
103
|
+
check(pairs, ':', MSG_NO_MIXED_KEYS)
|
104
104
|
end
|
105
105
|
end
|
106
106
|
|
@@ -34,6 +34,10 @@ module RuboCop
|
|
34
34
|
add_offense(node) do |corrector|
|
35
35
|
if node.parent.find(&:kwoptarg_type?) == node
|
36
36
|
corrector.insert_before(node, "#{kwarg_nodes.map(&:source).join(', ')}, ")
|
37
|
+
|
38
|
+
arguments = node.each_ancestor(:def, :defs).first.arguments
|
39
|
+
append_newline_to_last_kwoptarg(arguments, corrector) unless parentheses?(arguments)
|
40
|
+
|
37
41
|
remove_kwargs(kwarg_nodes, corrector)
|
38
42
|
end
|
39
43
|
end
|
@@ -41,6 +45,14 @@ module RuboCop
|
|
41
45
|
|
42
46
|
private
|
43
47
|
|
48
|
+
def append_newline_to_last_kwoptarg(arguments, corrector)
|
49
|
+
last_argument = arguments.last
|
50
|
+
return if last_argument.kwrestarg_type? || last_argument.blockarg_type?
|
51
|
+
|
52
|
+
last_kwoptarg = arguments.reverse.find(&:kwoptarg_type?)
|
53
|
+
corrector.insert_after(last_kwoptarg, "\n")
|
54
|
+
end
|
55
|
+
|
44
56
|
def remove_kwargs(kwarg_nodes, corrector)
|
45
57
|
kwarg_nodes.each do |kwarg|
|
46
58
|
with_space = range_with_surrounding_space(range: kwarg.source_range)
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# This cop checks for uses of `if-else` and ternary operators with a negated condition
|
7
|
+
# which can be simplified by inverting condition and swapping branches.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# if !x
|
12
|
+
# do_something
|
13
|
+
# else
|
14
|
+
# do_something_else
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# if x
|
19
|
+
# do_something_else
|
20
|
+
# else
|
21
|
+
# do_something
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# # bad
|
25
|
+
# !x ? do_something : do_something_else
|
26
|
+
#
|
27
|
+
# # good
|
28
|
+
# x ? do_something_else : do_something
|
29
|
+
#
|
30
|
+
class NegatedIfElseCondition < Base
|
31
|
+
extend AutoCorrector
|
32
|
+
|
33
|
+
MSG = 'Invert the negated condition and swap the %<type>s branches.'
|
34
|
+
|
35
|
+
NEGATED_EQUALITY_METHODS = %i[!= !~].freeze
|
36
|
+
|
37
|
+
def self.autocorrect_incompatible_with
|
38
|
+
[Style::InverseMethods, Style::Not]
|
39
|
+
end
|
40
|
+
|
41
|
+
def on_new_investigation
|
42
|
+
@corrected_nodes = nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def on_if(node)
|
46
|
+
return unless if_else?(node)
|
47
|
+
|
48
|
+
condition = node.condition
|
49
|
+
return unless negated_condition?(condition)
|
50
|
+
|
51
|
+
type = node.ternary? ? 'ternary' : 'if-else'
|
52
|
+
add_offense(node, message: format(MSG, type: type)) do |corrector|
|
53
|
+
unless corrected_ancestor?(node)
|
54
|
+
correct_negated_condition(corrector, condition)
|
55
|
+
swap_branches(corrector, node)
|
56
|
+
|
57
|
+
@corrected_nodes ||= Set.new.compare_by_identity
|
58
|
+
@corrected_nodes.add(node)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def if_else?(node)
|
66
|
+
else_branch = node.else_branch
|
67
|
+
!node.elsif? && else_branch && (!else_branch.if_type? || !else_branch.elsif?)
|
68
|
+
end
|
69
|
+
|
70
|
+
def negated_condition?(node)
|
71
|
+
node.send_type? &&
|
72
|
+
(node.negation_method? || NEGATED_EQUALITY_METHODS.include?(node.method_name))
|
73
|
+
end
|
74
|
+
|
75
|
+
def corrected_ancestor?(node)
|
76
|
+
node.each_ancestor(:if).any? { |ancestor| @corrected_nodes&.include?(ancestor) }
|
77
|
+
end
|
78
|
+
|
79
|
+
def correct_negated_condition(corrector, node)
|
80
|
+
receiver, method_name, rhs = *node
|
81
|
+
replacement =
|
82
|
+
if node.negation_method?
|
83
|
+
receiver.source
|
84
|
+
else
|
85
|
+
inverted_method = method_name.to_s.sub('!', '=')
|
86
|
+
"#{receiver.source} #{inverted_method} #{rhs.source}"
|
87
|
+
end
|
88
|
+
|
89
|
+
corrector.replace(node, replacement)
|
90
|
+
end
|
91
|
+
|
92
|
+
def swap_branches(corrector, node)
|
93
|
+
corrector.replace(node.if_branch, node.else_branch.source)
|
94
|
+
corrector.replace(node.else_branch, node.if_branch.source)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -13,25 +13,32 @@ module RuboCop
|
|
13
13
|
# will also suggest constructing error objects when the exception is
|
14
14
|
# passed multiple arguments.
|
15
15
|
#
|
16
|
+
# The exploded style has an `AllowedCompactTypes` configuration
|
17
|
+
# option that takes an Array of exception name Strings.
|
18
|
+
#
|
16
19
|
# @example EnforcedStyle: exploded (default)
|
17
20
|
# # bad
|
18
|
-
# raise StandardError.new(
|
21
|
+
# raise StandardError.new('message')
|
19
22
|
#
|
20
23
|
# # good
|
21
|
-
# raise StandardError,
|
22
|
-
# fail
|
24
|
+
# raise StandardError, 'message'
|
25
|
+
# fail 'message'
|
23
26
|
# raise MyCustomError.new(arg1, arg2, arg3)
|
24
27
|
# raise MyKwArgError.new(key1: val1, key2: val2)
|
25
28
|
#
|
29
|
+
# # With `AllowedCompactTypes` set to ['MyWrappedError']
|
30
|
+
# raise MyWrappedError.new(obj)
|
31
|
+
# raise MyWrappedError.new(obj), 'message'
|
32
|
+
#
|
26
33
|
# @example EnforcedStyle: compact
|
27
34
|
# # bad
|
28
|
-
# raise StandardError,
|
35
|
+
# raise StandardError, 'message'
|
29
36
|
# raise RuntimeError, arg1, arg2, arg3
|
30
37
|
#
|
31
38
|
# # good
|
32
|
-
# raise StandardError.new(
|
39
|
+
# raise StandardError.new('message')
|
33
40
|
# raise MyCustomError.new(arg1, arg2, arg3)
|
34
|
-
# fail
|
41
|
+
# fail 'message'
|
35
42
|
class RaiseArgs < Base
|
36
43
|
include ConfigurableEnforcedStyle
|
37
44
|
extend AutoCorrector
|
@@ -102,6 +109,8 @@ module RuboCop
|
|
102
109
|
return unless first_arg.send_type? && first_arg.method?(:new)
|
103
110
|
return if acceptable_exploded_args?(first_arg.arguments)
|
104
111
|
|
112
|
+
return if allowed_non_exploded_type?(first_arg)
|
113
|
+
|
105
114
|
add_offense(node, message: format(EXPLODED_MSG, method: node.method_name)) do |corrector|
|
106
115
|
replacement = correction_compact_to_exploded(node)
|
107
116
|
|
@@ -123,6 +132,12 @@ module RuboCop
|
|
123
132
|
arg.hash_type? || arg.splat_type?
|
124
133
|
end
|
125
134
|
|
135
|
+
def allowed_non_exploded_type?(arg)
|
136
|
+
type = arg.receiver.const_name
|
137
|
+
|
138
|
+
Array(cop_config['AllowedCompactTypes']).include?(type)
|
139
|
+
end
|
140
|
+
|
126
141
|
def requires_parens?(parent)
|
127
142
|
parent.and_type? || parent.or_type? ||
|
128
143
|
parent.if_type? && parent.ternary?
|
@@ -82,7 +82,7 @@ module RuboCop
|
|
82
82
|
|
83
83
|
def each_escape(node)
|
84
84
|
node.parsed_tree&.traverse&.reduce(0) do |char_class_depth, (event, expr)|
|
85
|
-
yield(expr.text[1], expr.
|
85
|
+
yield(expr.text[1], expr.start_index, !char_class_depth.zero?) if expr.type == :escape
|
86
86
|
|
87
87
|
if expr.type == :set
|
88
88
|
char_class_depth + (event == :enter ? 1 : -1)
|
data/lib/rubocop/cop/util.rb
CHANGED
@@ -19,13 +19,18 @@ module RuboCop
|
|
19
19
|
super
|
20
20
|
|
21
21
|
str = with_interpolations_blanked
|
22
|
-
|
23
|
-
Regexp::Parser.parse(str, options: options)
|
22
|
+
begin
|
23
|
+
@parsed_tree = Regexp::Parser.parse(str, options: options)
|
24
24
|
rescue StandardError
|
25
|
-
nil
|
25
|
+
@parsed_tree = nil
|
26
|
+
else
|
27
|
+
origin = loc.begin.end
|
28
|
+
source = @parsed_tree.to_s
|
29
|
+
@parsed_tree.each_expression(true) do |e|
|
30
|
+
e.origin = origin
|
31
|
+
e.source = source
|
32
|
+
end
|
26
33
|
end
|
27
|
-
origin = loc.begin.end
|
28
|
-
@parsed_tree&.each_expression(true) { |e| e.origin = origin }
|
29
34
|
end
|
30
35
|
|
31
36
|
def each_capture(named: ANY)
|
@@ -20,11 +20,18 @@ module RuboCop
|
|
20
20
|
module Expression
|
21
21
|
# Add `expression` and `loc` to all `regexp_parser` nodes
|
22
22
|
module Base
|
23
|
-
attr_accessor :origin
|
23
|
+
attr_accessor :origin, :source
|
24
|
+
|
25
|
+
def start_index
|
26
|
+
# ts is a byte index; convert it to a character index
|
27
|
+
@start_index ||= source.byteslice(0, ts).length
|
28
|
+
end
|
24
29
|
|
25
30
|
# Shortcut to `loc.expression`
|
26
31
|
def expression
|
27
|
-
@expression ||=
|
32
|
+
@expression ||= begin
|
33
|
+
origin.adjust(begin_pos: start_index, end_pos: start_index + full_length)
|
34
|
+
end
|
28
35
|
end
|
29
36
|
|
30
37
|
# @returns a location map like `parser` does, with:
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Formatter
|
5
|
+
# This formatter formats report data as GitHub Workflow commands resulting
|
6
|
+
# in GitHub check annotations when run within GitHub Actions.
|
7
|
+
class GitHubActionsFormatter < BaseFormatter
|
8
|
+
ESCAPE_MAP = {
|
9
|
+
'%' => '%25',
|
10
|
+
"\n" => '%0A',
|
11
|
+
"\r" => '%0D'
|
12
|
+
}.freeze
|
13
|
+
|
14
|
+
def file_finished(file, offenses)
|
15
|
+
offenses.each { |offense| report_offense(file, offense) }
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def github_escape(string)
|
21
|
+
string.gsub(Regexp.union(ESCAPE_MAP.keys), ESCAPE_MAP)
|
22
|
+
end
|
23
|
+
|
24
|
+
def minimum_severity_to_fail
|
25
|
+
@minimum_severity_to_fail ||= begin
|
26
|
+
name = options.fetch(:fail_level, :refactor)
|
27
|
+
RuboCop::Cop::Severity.new(name)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def github_severity(offense)
|
32
|
+
offense.severity < minimum_severity_to_fail ? 'warning' : 'error'
|
33
|
+
end
|
34
|
+
|
35
|
+
def report_offense(file, offense)
|
36
|
+
output.printf(
|
37
|
+
"\n::%<severity>s file=%<file>s,line=%<line>d,col=%<column>d::%<message>s\n",
|
38
|
+
severity: github_severity(offense),
|
39
|
+
file: file,
|
40
|
+
line: offense.line,
|
41
|
+
column: offense.real_column,
|
42
|
+
message: github_escape(offense.message)
|
43
|
+
)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|