rubocop 0.87.1 → 0.88.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 +1 -1
- data/bin/rubocop-profile +31 -0
- data/config/default.yml +57 -6
- data/lib/rubocop.rb +6 -0
- data/lib/rubocop/cli.rb +2 -2
- data/lib/rubocop/cli/command/auto_genenerate_config.rb +2 -2
- data/lib/rubocop/config_loader.rb +20 -7
- data/lib/rubocop/config_store.rb +4 -0
- data/lib/rubocop/cop/autocorrect_logic.rb +1 -1
- data/lib/rubocop/cop/badge.rb +1 -1
- data/lib/rubocop/cop/base.rb +12 -4
- data/lib/rubocop/cop/cop.rb +1 -1
- data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +26 -0
- data/lib/rubocop/cop/gemspec/required_ruby_version.rb +6 -1
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -0
- data/lib/rubocop/cop/layout/end_alignment.rb +3 -2
- data/lib/rubocop/cop/layout/multiline_block_layout.rb +16 -5
- data/lib/rubocop/cop/layout/space_around_block_parameters.rb +3 -2
- data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +27 -68
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +3 -2
- data/lib/rubocop/cop/lint/disjunctive_assignment_in_constructor.rb +8 -2
- data/lib/rubocop/cop/lint/duplicate_elsif_condition.rb +39 -0
- data/lib/rubocop/cop/lint/duplicate_methods.rb +2 -2
- data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +3 -2
- data/lib/rubocop/cop/lint/literal_as_condition.rb +11 -1
- data/lib/rubocop/cop/lint/nested_method_definition.rb +13 -19
- data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +67 -0
- data/lib/rubocop/cop/mixin/statement_modifier.rb +3 -3
- data/lib/rubocop/cop/style/accessor_grouping.rb +8 -1
- data/lib/rubocop/cop/style/array_coercion.rb +63 -0
- data/lib/rubocop/cop/style/auto_resource_cleanup.rb +3 -2
- data/lib/rubocop/cop/style/bisected_attr_accessor.rb +5 -4
- data/lib/rubocop/cop/style/case_like_if.rb +217 -0
- data/lib/rubocop/cop/style/commented_keyword.rb +5 -2
- data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
- data/lib/rubocop/cop/style/exponential_notation.rb +6 -8
- data/lib/rubocop/cop/style/float_division.rb +7 -10
- data/lib/rubocop/cop/style/format_string_token.rb +5 -5
- data/lib/rubocop/cop/style/hash_as_last_array_item.rb +62 -0
- data/lib/rubocop/cop/style/hash_like_case.rb +76 -0
- data/lib/rubocop/cop/style/if_unless_modifier.rb +11 -11
- data/lib/rubocop/cop/style/missing_else.rb +1 -11
- data/lib/rubocop/cop/style/numeric_predicate.rb +3 -4
- data/lib/rubocop/cop/style/parallel_assignment.rb +3 -3
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +1 -1
- data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +50 -0
- data/lib/rubocop/cop/style/redundant_sort.rb +3 -2
- data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +3 -2
- data/lib/rubocop/cop/style/trailing_method_end_statement.rb +9 -32
- data/lib/rubocop/cop/variable_force/variable.rb +5 -3
- data/lib/rubocop/file_finder.rb +12 -12
- data/lib/rubocop/path_util.rb +2 -17
- data/lib/rubocop/result_cache.rb +12 -8
- data/lib/rubocop/rspec/expect_offense.rb +31 -5
- data/lib/rubocop/rspec/shared_contexts.rb +12 -9
- data/lib/rubocop/runner.rb +5 -6
- data/lib/rubocop/target_finder.rb +2 -2
- data/lib/rubocop/version.rb +1 -1
- metadata +9 -2
@@ -75,11 +75,11 @@ module RuboCop
|
|
75
75
|
|
76
76
|
# rubocop:disable Style/FormatStringToken
|
77
77
|
def message_text(style)
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
78
|
+
{
|
79
|
+
annotated: 'annotated tokens (like `%<foo>s`)',
|
80
|
+
template: 'template tokens (like `%{foo}`)',
|
81
|
+
unannotated: 'unannotated tokens (like `%s`)'
|
82
|
+
}[style]
|
83
83
|
end
|
84
84
|
# rubocop:enable Style/FormatStringToken
|
85
85
|
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Checks for presence or absence of braces around hash literal as a last
|
7
|
+
# array item depending on configuration.
|
8
|
+
#
|
9
|
+
# @example EnforcedStyle: braces (default)
|
10
|
+
# # bad
|
11
|
+
# [1, 2, one: 1, two: 2]
|
12
|
+
#
|
13
|
+
# # good
|
14
|
+
# [1, 2, { one: 1, two: 2 }]
|
15
|
+
#
|
16
|
+
# @example EnforcedStyle: no_braces
|
17
|
+
# # bad
|
18
|
+
# [1, 2, { one: 1, two: 2 }]
|
19
|
+
#
|
20
|
+
# # good
|
21
|
+
# [1, 2, one: 1, two: 2]
|
22
|
+
#
|
23
|
+
class HashAsLastArrayItem < Base
|
24
|
+
include ConfigurableEnforcedStyle
|
25
|
+
extend AutoCorrector
|
26
|
+
|
27
|
+
def on_hash(node)
|
28
|
+
return unless node.parent&.array_type?
|
29
|
+
|
30
|
+
if braces_style?
|
31
|
+
check_braces(node)
|
32
|
+
else
|
33
|
+
check_no_braces(node)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def check_braces(node)
|
40
|
+
return if node.braces?
|
41
|
+
|
42
|
+
add_offense(node, message: 'Wrap hash in `{` and `}`.') do |corrector|
|
43
|
+
corrector.wrap(node, '{', '}')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def check_no_braces(node)
|
48
|
+
return unless node.braces?
|
49
|
+
|
50
|
+
add_offense(node, message: 'Omit the braces around the hash.') do |corrector|
|
51
|
+
corrector.remove(node.loc.begin)
|
52
|
+
corrector.remove(node.loc.end)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def braces_style?
|
57
|
+
style == :braces
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# This cop checks for places where `case-when` represents a simple 1:1
|
7
|
+
# mapping and can be replaced with a hash lookup.
|
8
|
+
#
|
9
|
+
# @example MinBranchesCount: 3 (default)
|
10
|
+
# # bad
|
11
|
+
# case country
|
12
|
+
# when 'europe'
|
13
|
+
# 'http://eu.example.com'
|
14
|
+
# when 'america'
|
15
|
+
# 'http://us.example.com'
|
16
|
+
# when 'australia'
|
17
|
+
# 'http://au.example.com'
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# # good
|
21
|
+
# SITES = {
|
22
|
+
# 'europe' => 'http://eu.example.com',
|
23
|
+
# 'america' => 'http://us.example.com',
|
24
|
+
# 'australia' => 'http://au.example.com'
|
25
|
+
# }
|
26
|
+
# SITES[country]
|
27
|
+
#
|
28
|
+
# @example MinBranchesCount: 4
|
29
|
+
# # good
|
30
|
+
# case country
|
31
|
+
# when 'europe'
|
32
|
+
# 'http://eu.example.com'
|
33
|
+
# when 'america'
|
34
|
+
# 'http://us.example.com'
|
35
|
+
# when 'australia'
|
36
|
+
# 'http://au.example.com'
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
class HashLikeCase < Base
|
40
|
+
MSG = 'Consider replacing `case-when` with a hash lookup.'
|
41
|
+
|
42
|
+
def_node_matcher :hash_like_case?, <<~PATTERN
|
43
|
+
(case
|
44
|
+
_
|
45
|
+
(when
|
46
|
+
${str_type? sym_type?}
|
47
|
+
$[!nil? recursive_basic_literal?])+ nil?)
|
48
|
+
PATTERN
|
49
|
+
|
50
|
+
def on_case(node)
|
51
|
+
return if node.when_branches.size < min_branches_count
|
52
|
+
|
53
|
+
hash_like_case?(node) do |condition_nodes, body_nodes|
|
54
|
+
if nodes_of_same_type?(condition_nodes) &&
|
55
|
+
nodes_of_same_type?(body_nodes)
|
56
|
+
add_offense(node)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def nodes_of_same_type?(nodes)
|
64
|
+
nodes.all? { |node| node.type == nodes.first.type }
|
65
|
+
end
|
66
|
+
|
67
|
+
def min_branches_count
|
68
|
+
length = cop_config['MinBranchesCount'] || 3
|
69
|
+
return length if length.is_a?(Integer) && length.positive?
|
70
|
+
|
71
|
+
raise 'MinBranchesCount needs to be a positive integer!'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -41,11 +41,8 @@ module RuboCop
|
|
41
41
|
MSG_USE_NORMAL =
|
42
42
|
'Modifier form of `%<keyword>s` makes the line too long.'
|
43
43
|
|
44
|
-
ASSIGNMENT_TYPES = %i[lvasgn casgn cvasgn
|
45
|
-
gvasgn ivasgn masgn].freeze
|
46
|
-
|
47
44
|
def on_if(node)
|
48
|
-
msg = if
|
45
|
+
msg = if single_line_as_modifier?(node)
|
49
46
|
MSG_USE_MODIFIER unless named_capture_in_condition?(node)
|
50
47
|
elsif too_long_due_to_modifier?(node)
|
51
48
|
MSG_USE_NORMAL
|
@@ -125,13 +122,15 @@ module RuboCop
|
|
125
122
|
node.condition.match_with_lvasgn_type?
|
126
123
|
end
|
127
124
|
|
128
|
-
def
|
129
|
-
|
130
|
-
|
125
|
+
def non_eligible_node?(node)
|
126
|
+
non_simple_if_unless?(node) ||
|
127
|
+
node.chained? ||
|
128
|
+
node.nested_conditional? ||
|
129
|
+
super
|
131
130
|
end
|
132
131
|
|
133
|
-
def
|
134
|
-
node.ternary? || node.
|
132
|
+
def non_simple_if_unless?(node)
|
133
|
+
node.ternary? || node.elsif? || node.else?
|
135
134
|
end
|
136
135
|
|
137
136
|
def another_statement_on_same_line?(node)
|
@@ -153,8 +152,9 @@ module RuboCop
|
|
153
152
|
# Parenthesize corrected expression if changing to modifier-if form
|
154
153
|
# would change the meaning of the parent expression
|
155
154
|
# (due to the low operator precedence of modifier-if)
|
156
|
-
|
157
|
-
return
|
155
|
+
parent = node.parent
|
156
|
+
return false if parent.nil?
|
157
|
+
return true if parent.assignment? || parent.operator_keyword?
|
158
158
|
|
159
159
|
node.parent.send_type? && !node.parent.parenthesized?
|
160
160
|
end
|
@@ -119,17 +119,7 @@ module RuboCop
|
|
119
119
|
private
|
120
120
|
|
121
121
|
def check(node)
|
122
|
-
|
123
|
-
|
124
|
-
if empty_else_cop_enabled?
|
125
|
-
if empty_else_style == :empty
|
126
|
-
add_offense(node)
|
127
|
-
elsif empty_else_style == :nil
|
128
|
-
add_offense(node)
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
add_offense(node)
|
122
|
+
add_offense(node) unless node.else?
|
133
123
|
end
|
134
124
|
|
135
125
|
def message(node)
|
@@ -54,15 +54,14 @@ module RuboCop
|
|
54
54
|
}.freeze
|
55
55
|
|
56
56
|
def on_send(node)
|
57
|
+
numeric, replacement = check(node)
|
58
|
+
return unless numeric
|
59
|
+
|
57
60
|
return if ignored_method?(node.method_name) ||
|
58
61
|
node.each_ancestor(:send, :block).any? do |ancestor|
|
59
62
|
ignored_method?(ancestor.method_name)
|
60
63
|
end
|
61
64
|
|
62
|
-
numeric, replacement = check(node)
|
63
|
-
|
64
|
-
return unless numeric
|
65
|
-
|
66
65
|
add_offense(node,
|
67
66
|
message: format(MSG,
|
68
67
|
prefer: replacement,
|
@@ -30,7 +30,7 @@ module RuboCop
|
|
30
30
|
def on_masgn(node)
|
31
31
|
lhs, rhs = *node
|
32
32
|
lhs_elements = *lhs
|
33
|
-
rhs_elements =
|
33
|
+
rhs_elements = Array(rhs).compact # edge case for one constant
|
34
34
|
|
35
35
|
return if allowed_lhs?(lhs) || allowed_rhs?(rhs) ||
|
36
36
|
allowed_masign?(lhs_elements, rhs_elements)
|
@@ -42,7 +42,7 @@ module RuboCop
|
|
42
42
|
lambda do |corrector|
|
43
43
|
left, right = *node
|
44
44
|
left_elements = *left
|
45
|
-
right_elements =
|
45
|
+
right_elements = Array(right).compact
|
46
46
|
order = find_valid_order(left_elements, right_elements)
|
47
47
|
correction = assignment_corrector(node, order)
|
48
48
|
|
@@ -69,7 +69,7 @@ module RuboCop
|
|
69
69
|
|
70
70
|
def allowed_rhs?(node)
|
71
71
|
# Edge case for one constant
|
72
|
-
elements =
|
72
|
+
elements = Array(node).compact
|
73
73
|
|
74
74
|
# Account for edge case of `Constant::CONSTANT`
|
75
75
|
!node.array_type? ||
|
@@ -102,7 +102,7 @@ module RuboCop
|
|
102
102
|
delimiters_regexp = Regexp.union(delimiters)
|
103
103
|
node
|
104
104
|
.children.map { |n| string_source(n) }.compact
|
105
|
-
.any? { |s| delimiters_regexp.match?(s) }
|
105
|
+
.any? { |s| delimiters_regexp.match?(s.scrub) }
|
106
106
|
end
|
107
107
|
|
108
108
|
def string_source(node)
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# This cop checks for the presence of superfluous `.rb` extension in
|
7
|
+
# the filename provided to `require` and `require_relative`.
|
8
|
+
#
|
9
|
+
# Note: If the extension is omitted, Ruby tries adding '.rb', '.so',
|
10
|
+
# and so on to the name until found. If the file named cannot be found,
|
11
|
+
# a `LoadError` will be raised.
|
12
|
+
# There is an edge case where `foo.so` file is loaded instead of a `LoadError`
|
13
|
+
# if `foo.so` file exists when `require 'foo.rb'` will be changed to `require 'foo'`,
|
14
|
+
# but that seems harmless.
|
15
|
+
#
|
16
|
+
# @example
|
17
|
+
# # bad
|
18
|
+
# require 'foo.rb'
|
19
|
+
# require_relative '../foo.rb'
|
20
|
+
#
|
21
|
+
# # good
|
22
|
+
# require 'foo'
|
23
|
+
# require 'foo.so'
|
24
|
+
# require_relative '../foo'
|
25
|
+
# require_relative '../foo.so'
|
26
|
+
#
|
27
|
+
class RedundantFileExtensionInRequire < Cop
|
28
|
+
MSG = 'Redundant `.rb` file extension detected.'
|
29
|
+
|
30
|
+
def_node_matcher :require_call?, <<~PATTERN
|
31
|
+
(send nil? {:require :require_relative} $str_type?)
|
32
|
+
PATTERN
|
33
|
+
|
34
|
+
def on_send(node)
|
35
|
+
require_call?(node) do |name_node|
|
36
|
+
add_offense(name_node) if name_node.value.end_with?('.rb')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def autocorrect(node)
|
41
|
+
correction = node.value.sub(/\.rb\z/, '')
|
42
|
+
|
43
|
+
lambda do |corrector|
|
44
|
+
corrector.replace(node, "'#{correction}'")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -34,9 +34,10 @@ module RuboCop
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def autocorrect(node)
|
37
|
-
|
37
|
+
case style
|
38
|
+
when :require_parentheses
|
38
39
|
missing_parentheses_corrector(node)
|
39
|
-
|
40
|
+
when :require_no_parentheses
|
40
41
|
unwanted_parentheses_corrector(node)
|
41
42
|
end
|
42
43
|
end
|
@@ -33,8 +33,8 @@ module RuboCop
|
|
33
33
|
# end
|
34
34
|
# end
|
35
35
|
#
|
36
|
-
class TrailingMethodEndStatement <
|
37
|
-
|
36
|
+
class TrailingMethodEndStatement < Base
|
37
|
+
extend AutoCorrector
|
38
38
|
|
39
39
|
MSG = 'Place the end statement of a multi-line method on ' \
|
40
40
|
'its own line.'
|
@@ -42,13 +42,11 @@ module RuboCop
|
|
42
42
|
def on_def(node)
|
43
43
|
return unless trailing_end?(node)
|
44
44
|
|
45
|
-
add_offense(node
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
break_line_before_end(node, corrector)
|
51
|
-
remove_semicolon(node, corrector)
|
45
|
+
add_offense(node.loc.end) do |corrector|
|
46
|
+
corrector.insert_before(
|
47
|
+
node.loc.end,
|
48
|
+
"\n" + ' ' * node.loc.keyword.column
|
49
|
+
)
|
52
50
|
end
|
53
51
|
end
|
54
52
|
|
@@ -60,30 +58,9 @@ module RuboCop
|
|
60
58
|
body_and_end_on_same_line?(node)
|
61
59
|
end
|
62
60
|
|
63
|
-
def end_token(node)
|
64
|
-
tokens(node).reverse.find(&:end?)
|
65
|
-
end
|
66
|
-
|
67
61
|
def body_and_end_on_same_line?(node)
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
def token_before_end(node)
|
72
|
-
i = tokens(node).index(end_token(node))
|
73
|
-
tokens(node)[i - 1]
|
74
|
-
end
|
75
|
-
|
76
|
-
def break_line_before_end(node, corrector)
|
77
|
-
corrector.insert_before(
|
78
|
-
end_token(node).pos,
|
79
|
-
"\n" + ' ' * configured_indentation_width
|
80
|
-
)
|
81
|
-
end
|
82
|
-
|
83
|
-
def remove_semicolon(node, corrector)
|
84
|
-
return unless token_before_end(node).semicolon?
|
85
|
-
|
86
|
-
corrector.remove(token_before_end(node).pos)
|
62
|
+
last_child = node.children.last
|
63
|
+
last_child.loc.last_line == node.loc.end.last_line
|
87
64
|
end
|
88
65
|
end
|
89
66
|
end
|
@@ -42,10 +42,10 @@ module RuboCop
|
|
42
42
|
def reference!(node)
|
43
43
|
reference = Reference.new(node, @scope)
|
44
44
|
@references << reference
|
45
|
-
consumed_branches =
|
45
|
+
consumed_branches = nil
|
46
46
|
|
47
47
|
@assignments.reverse_each do |assignment|
|
48
|
-
next if consumed_branches
|
48
|
+
next if consumed_branches&.include?(assignment.branch)
|
49
49
|
|
50
50
|
assignment.reference!(node) unless assignment.run_exclusively_with?(reference)
|
51
51
|
|
@@ -58,7 +58,9 @@ module RuboCop
|
|
58
58
|
|
59
59
|
break if !assignment.branch || assignment.branch == reference.branch
|
60
60
|
|
61
|
-
|
61
|
+
unless assignment.branch.may_run_incompletely?
|
62
|
+
(consumed_branches ||= Set.new) << assignment.branch
|
63
|
+
end
|
62
64
|
end
|
63
65
|
end
|
64
66
|
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
data/lib/rubocop/file_finder.rb
CHANGED
@@ -9,33 +9,33 @@ module RuboCop
|
|
9
9
|
@root_level = level
|
10
10
|
end
|
11
11
|
|
12
|
-
def self.root_level?(path)
|
13
|
-
@root_level == path.to_s
|
12
|
+
def self.root_level?(path, stop_dir)
|
13
|
+
(@root_level || stop_dir) == path.to_s
|
14
14
|
end
|
15
15
|
|
16
|
-
def find_file_upwards(filename, start_dir)
|
17
|
-
traverse_files_upwards(filename, start_dir) do |file|
|
16
|
+
def find_file_upwards(filename, start_dir, stop_dir = nil)
|
17
|
+
traverse_files_upwards(filename, start_dir, stop_dir) do |file|
|
18
18
|
# minimize iteration for performance
|
19
19
|
return file if file
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
def
|
24
|
-
|
25
|
-
traverse_files_upwards(filename, start_dir) do |file|
|
26
|
-
|
23
|
+
def find_last_file_upwards(filename, start_dir, stop_dir = nil)
|
24
|
+
last_file = nil
|
25
|
+
traverse_files_upwards(filename, start_dir, stop_dir) do |file|
|
26
|
+
last_file = file
|
27
27
|
end
|
28
|
-
|
28
|
+
last_file
|
29
29
|
end
|
30
30
|
|
31
31
|
private
|
32
32
|
|
33
|
-
def traverse_files_upwards(filename, start_dir)
|
33
|
+
def traverse_files_upwards(filename, start_dir, stop_dir)
|
34
34
|
Pathname.new(start_dir).expand_path.ascend do |dir|
|
35
|
-
break if FileFinder.root_level?(dir)
|
36
|
-
|
37
35
|
file = dir + filename
|
38
36
|
yield(file.to_s) if file.exist?
|
37
|
+
|
38
|
+
break if FileFinder.root_level?(dir, stop_dir)
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|