rubocop 1.8.1 → 1.9.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 +1 -1
- data/config/default.yml +34 -4
- data/lib/rubocop.rb +5 -0
- data/lib/rubocop/cli/command/auto_genenerate_config.rb +5 -4
- data/lib/rubocop/config.rb +2 -2
- data/lib/rubocop/config_loader.rb +7 -14
- data/lib/rubocop/config_store.rb +12 -1
- data/lib/rubocop/cop/base.rb +1 -1
- data/lib/rubocop/cop/generator.rb +1 -3
- data/lib/rubocop/cop/internal_affairs.rb +5 -1
- data/lib/rubocop/cop/internal_affairs/empty_line_between_expect_offense_and_correction.rb +68 -0
- data/lib/rubocop/cop/internal_affairs/example_description.rb +89 -0
- data/lib/rubocop/cop/internal_affairs/redundant_described_class_as_subject.rb +61 -0
- data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +64 -0
- data/lib/rubocop/cop/layout/class_structure.rb +7 -2
- data/lib/rubocop/cop/lint/number_conversion.rb +41 -6
- data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +47 -0
- data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +39 -0
- data/lib/rubocop/cop/lint/symbol_conversion.rb +102 -0
- data/lib/rubocop/cop/lint/triple_quotes.rb +71 -0
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +5 -0
- data/lib/rubocop/cop/mixin/comments_help.rb +0 -1
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +38 -5
- data/lib/rubocop/cop/naming/variable_number.rb +1 -1
- data/lib/rubocop/cop/severity.rb +3 -3
- data/lib/rubocop/cop/style/ascii_comments.rb +1 -1
- data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +49 -9
- data/lib/rubocop/cop/style/eval_with_location.rb +63 -34
- data/lib/rubocop/cop/style/float_division.rb +3 -0
- data/lib/rubocop/cop/style/format_string_token.rb +18 -2
- data/lib/rubocop/cop/style/if_inside_else.rb +14 -7
- data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +96 -0
- data/lib/rubocop/cop/style/nil_comparison.rb +1 -0
- data/lib/rubocop/cop/style/non_nil_check.rb +23 -13
- data/lib/rubocop/cop/style/single_line_methods.rb +1 -1
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +26 -2
- data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
- data/lib/rubocop/formatter/git_hub_actions_formatter.rb +1 -0
- data/lib/rubocop/magic_comment.rb +30 -1
- data/lib/rubocop/options.rb +1 -1
- data/lib/rubocop/rspec/expect_offense.rb +5 -2
- data/lib/rubocop/runner.rb +1 -0
- data/lib/rubocop/version.rb +2 -2
- metadata +12 -3
@@ -69,6 +69,11 @@ module RuboCop
|
|
69
69
|
# @api private
|
70
70
|
def extract_first_element_over_column_limit(node, elements, max)
|
71
71
|
line = node.first_line
|
72
|
+
|
73
|
+
# If the first argument is a hash pair but the method is not parenthesized,
|
74
|
+
# the argument cannot be moved to another line because it cause a syntax error.
|
75
|
+
elements.shift if node.send_type? && !node.parenthesized? && elements.first.pair_type?
|
76
|
+
|
72
77
|
i = 0
|
73
78
|
i += 1 while within_column_limit?(elements[i], max, line)
|
74
79
|
return elements.first if i.zero?
|
@@ -71,11 +71,7 @@ module RuboCop
|
|
71
71
|
add_offense(range, message: message) do |corrector|
|
72
72
|
corrector.replace(range, preferred_name)
|
73
73
|
|
74
|
-
node.body
|
75
|
-
next unless var.children.first == offending_name
|
76
|
-
|
77
|
-
corrector.replace(var, preferred_name)
|
78
|
-
end
|
74
|
+
correct_node(corrector, node.body, offending_name, preferred_name)
|
79
75
|
end
|
80
76
|
end
|
81
77
|
|
@@ -86,6 +82,43 @@ module RuboCop
|
|
86
82
|
variable.loc.expression
|
87
83
|
end
|
88
84
|
|
85
|
+
def variable_name_matches?(node, name)
|
86
|
+
if node.masgn_type?
|
87
|
+
node.each_descendant(:lvasgn).any? do |lvasgn_node|
|
88
|
+
variable_name_matches?(lvasgn_node, name)
|
89
|
+
end
|
90
|
+
else
|
91
|
+
node.children.first == name
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def correct_node(corrector, node, offending_name, preferred_name)
|
96
|
+
return unless node
|
97
|
+
|
98
|
+
node.each_node(:lvar, :lvasgn, :masgn) do |child_node|
|
99
|
+
next unless variable_name_matches?(child_node, offending_name)
|
100
|
+
|
101
|
+
corrector.replace(child_node, preferred_name) if child_node.lvar_type?
|
102
|
+
|
103
|
+
if child_node.masgn_type? || child_node.lvasgn_type?
|
104
|
+
correct_reassignment(corrector, child_node, offending_name, preferred_name)
|
105
|
+
break
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# If the exception variable is reassigned, that assignment needs to be corrected.
|
111
|
+
# Further `lvar` nodes will not be corrected though since they now refer to a
|
112
|
+
# different variable.
|
113
|
+
def correct_reassignment(corrector, node, offending_name, preferred_name)
|
114
|
+
if node.lvasgn_type?
|
115
|
+
correct_node(corrector, node.child_nodes.first, offending_name, preferred_name)
|
116
|
+
elsif node.masgn_type?
|
117
|
+
# With multiple assign, the assignments are in an array as the last child
|
118
|
+
correct_node(corrector, node.children.last, offending_name, preferred_name)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
89
122
|
def preferred_name(variable_name)
|
90
123
|
preferred_name = cop_config.fetch('PreferredName', 'e')
|
91
124
|
if variable_name.to_s.start_with?('_')
|
data/lib/rubocop/cop/severity.rb
CHANGED
@@ -6,10 +6,10 @@ module RuboCop
|
|
6
6
|
class Severity
|
7
7
|
include Comparable
|
8
8
|
|
9
|
-
NAMES = %i[refactor convention warning error fatal].freeze
|
9
|
+
NAMES = %i[info refactor convention warning error fatal].freeze
|
10
10
|
|
11
11
|
# @api private
|
12
|
-
CODE_TABLE = { R: :refactor, C: :convention,
|
12
|
+
CODE_TABLE = { I: :info, R: :refactor, C: :convention,
|
13
13
|
W: :warning, E: :error, F: :fatal }.freeze
|
14
14
|
|
15
15
|
# @api public
|
@@ -18,7 +18,7 @@ module RuboCop
|
|
18
18
|
#
|
19
19
|
# @return [Symbol]
|
20
20
|
# severity.
|
21
|
-
# any of `:refactor`, `:convention`, `:warning`, `:error` or `:fatal`.
|
21
|
+
# any of `:info`, `:refactor`, `:convention`, `:warning`, `:error` or `:fatal`.
|
22
22
|
attr_reader :name
|
23
23
|
|
24
24
|
def self.name_from_code(code)
|
@@ -7,7 +7,7 @@ module RuboCop
|
|
7
7
|
module Style
|
8
8
|
# This cop checks for non-ascii (non-English) characters
|
9
9
|
# in comments. You could set an array of allowed non-ascii chars in
|
10
|
-
# AllowedChars attribute (
|
10
|
+
# `AllowedChars` attribute (copyright notice "©" by default).
|
11
11
|
#
|
12
12
|
# @example
|
13
13
|
# # bad
|
@@ -9,37 +9,77 @@ module RuboCop
|
|
9
9
|
# This is useful if want to make sure that every RuboCop error gets fixed
|
10
10
|
# and not quickly disabled with a comment.
|
11
11
|
#
|
12
|
+
# Specific cops can be allowed with the `AllowedCops` configuration. Note that
|
13
|
+
# if this configuration is set, `rubocop:disable all` is still disallowed.
|
14
|
+
#
|
12
15
|
# @example
|
13
16
|
# # bad
|
14
17
|
# # rubocop:disable Metrics/AbcSize
|
15
|
-
# def
|
18
|
+
# def foo
|
16
19
|
# end
|
17
20
|
# # rubocop:enable Metrics/AbcSize
|
18
21
|
#
|
19
22
|
# # good
|
20
|
-
# def
|
23
|
+
# def foo
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# @example AllowedCops: [Metrics/AbcSize]
|
27
|
+
# # good
|
28
|
+
# # rubocop:disable Metrics/AbcSize
|
29
|
+
# def foo
|
21
30
|
# end
|
31
|
+
# # rubocop:enable Metrics/AbcSize
|
22
32
|
#
|
23
33
|
class DisableCopsWithinSourceCodeDirective < Base
|
24
34
|
extend AutoCorrector
|
25
35
|
|
26
36
|
# rubocop:enable Lint/RedundantCopDisableDirective
|
27
|
-
MSG = '
|
37
|
+
MSG = 'Rubocop disable/enable directives are not permitted.'
|
38
|
+
MSG_FOR_COPS = 'Rubocop disable/enable directives for %<cops>s are not permitted.'
|
28
39
|
|
29
40
|
def on_new_investigation
|
30
41
|
processed_source.comments.each do |comment|
|
31
|
-
|
42
|
+
directive_cops = directive_cops(comment)
|
43
|
+
disallowed_cops = directive_cops - allowed_cops
|
32
44
|
|
33
|
-
|
34
|
-
|
35
|
-
|
45
|
+
next unless disallowed_cops.any?
|
46
|
+
|
47
|
+
register_offense(comment, directive_cops, disallowed_cops)
|
36
48
|
end
|
37
49
|
end
|
38
50
|
|
39
51
|
private
|
40
52
|
|
41
|
-
def
|
42
|
-
|
53
|
+
def register_offense(comment, directive_cops, disallowed_cops)
|
54
|
+
message = if any_cops_allowed?
|
55
|
+
format(MSG_FOR_COPS, cops: "`#{disallowed_cops.join('`, `')}`")
|
56
|
+
else
|
57
|
+
MSG
|
58
|
+
end
|
59
|
+
|
60
|
+
add_offense(comment, message: message) do |corrector|
|
61
|
+
replacement = ''
|
62
|
+
|
63
|
+
if directive_cops.length != disallowed_cops.length
|
64
|
+
replacement = comment.text.sub(/#{Regexp.union(disallowed_cops)},?\s*/, '')
|
65
|
+
.sub(/,\s*$/, '')
|
66
|
+
end
|
67
|
+
|
68
|
+
corrector.replace(comment, replacement)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def directive_cops(comment)
|
73
|
+
match = CommentConfig::COMMENT_DIRECTIVE_REGEXP.match(comment.text)
|
74
|
+
match[2] ? match[2].split(',').map(&:strip) : []
|
75
|
+
end
|
76
|
+
|
77
|
+
def allowed_cops
|
78
|
+
Array(cop_config['AllowedCops'])
|
79
|
+
end
|
80
|
+
|
81
|
+
def any_cops_allowed?
|
82
|
+
allowed_cops.any?
|
43
83
|
end
|
44
84
|
end
|
45
85
|
end
|
@@ -3,9 +3,14 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Style
|
6
|
-
# This cop
|
7
|
-
#
|
8
|
-
#
|
6
|
+
# This cop ensures that eval methods (`eval`, `instance_eval`, `class_eval`
|
7
|
+
# and `module_eval`) are given filename and line number values (`__FILE__`
|
8
|
+
# and `__LINE__`). This data is used to ensure that any errors raised
|
9
|
+
# within the evaluated code will be given the correct identification
|
10
|
+
# in a backtrace.
|
11
|
+
#
|
12
|
+
# The cop also checks that the line number given relative to `__LINE__` is
|
13
|
+
# correct.
|
9
14
|
#
|
10
15
|
# @example
|
11
16
|
# # bad
|
@@ -32,27 +37,17 @@ module RuboCop
|
|
32
37
|
# end
|
33
38
|
# RUBY
|
34
39
|
class EvalWithLocation < Base
|
35
|
-
MSG = 'Pass `__FILE__` and `__LINE__` to
|
36
|
-
|
37
|
-
|
38
|
-
'
|
40
|
+
MSG = 'Pass `__FILE__` and `__LINE__` to `%<method_name>s`.'
|
41
|
+
MSG_EVAL = 'Pass a binding, `__FILE__` and `__LINE__` to `eval`.'
|
42
|
+
MSG_INCORRECT_FILE = 'Incorrect file for `%<method_name>s`; ' \
|
43
|
+
'use `%<expected>s` instead of `%<actual>s`.'
|
44
|
+
MSG_INCORRECT_LINE = 'Incorrect line number for `%<method_name>s`; ' \
|
45
|
+
'use `%<expected>s` instead of `%<actual>s`.'
|
39
46
|
|
40
47
|
RESTRICT_ON_SEND = %i[eval class_eval module_eval instance_eval].freeze
|
41
48
|
|
42
|
-
def_node_matcher :
|
43
|
-
{
|
44
|
-
(send nil? :eval ${str dstr})
|
45
|
-
(send nil? :eval ${str dstr} _)
|
46
|
-
(send nil? :eval ${str dstr} _ #special_file_keyword?)
|
47
|
-
(send nil? :eval ${str dstr} _ #special_file_keyword? _)
|
48
|
-
|
49
|
-
(send _ {:class_eval :module_eval :instance_eval}
|
50
|
-
${str dstr})
|
51
|
-
(send _ {:class_eval :module_eval :instance_eval}
|
52
|
-
${str dstr} #special_file_keyword?)
|
53
|
-
(send _ {:class_eval :module_eval :instance_eval}
|
54
|
-
${str dstr} #special_file_keyword? _)
|
55
|
-
}
|
49
|
+
def_node_matcher :valid_eval_receiver?, <<~PATTERN
|
50
|
+
{ nil? (const {nil? cbase} :Kernel) }
|
56
51
|
PATTERN
|
57
52
|
|
58
53
|
def_node_matcher :line_with_offset?, <<~PATTERN
|
@@ -63,17 +58,31 @@ module RuboCop
|
|
63
58
|
PATTERN
|
64
59
|
|
65
60
|
def on_send(node)
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
61
|
+
# Classes should not redefine eval, but in case one does, it shouldn't
|
62
|
+
# register an offense. Only `eval` without a receiver and `Kernel.eval`
|
63
|
+
# are considered.
|
64
|
+
return if node.method?(:eval) && !valid_eval_receiver?(node.receiver)
|
65
|
+
|
66
|
+
code = node.arguments.first
|
67
|
+
return unless code.str_type? || code.dstr_type?
|
68
|
+
|
69
|
+
file, line = file_and_line(node)
|
70
|
+
|
71
|
+
if line
|
72
|
+
check_file(node, file)
|
73
|
+
check_line(node, code)
|
74
|
+
else
|
75
|
+
register_offense(node)
|
72
76
|
end
|
73
77
|
end
|
74
78
|
|
75
79
|
private
|
76
80
|
|
81
|
+
def register_offense(node)
|
82
|
+
msg = node.method?(:eval) ? MSG_EVAL : format(MSG, method_name: node.method_name)
|
83
|
+
add_offense(node, message: msg)
|
84
|
+
end
|
85
|
+
|
77
86
|
def special_file_keyword?(node)
|
78
87
|
node.str_type? &&
|
79
88
|
node.source == '__FILE__'
|
@@ -84,6 +93,11 @@ module RuboCop
|
|
84
93
|
node.source == '__LINE__'
|
85
94
|
end
|
86
95
|
|
96
|
+
def file_and_line(node)
|
97
|
+
base = node.method?(:eval) ? 2 : 1
|
98
|
+
[node.arguments[base], node.arguments[base + 1]]
|
99
|
+
end
|
100
|
+
|
87
101
|
# FIXME: It's a Style/ConditionalAssignment's false positive.
|
88
102
|
# rubocop:disable Style/ConditionalAssignment
|
89
103
|
def with_lineno?(node)
|
@@ -95,17 +109,32 @@ module RuboCop
|
|
95
109
|
end
|
96
110
|
# rubocop:enable Style/ConditionalAssignment
|
97
111
|
|
98
|
-
def message_incorrect_line(actual, sign, line_diff)
|
112
|
+
def message_incorrect_line(method_name, actual, sign, line_diff)
|
99
113
|
expected =
|
100
114
|
if line_diff.zero?
|
101
115
|
'__LINE__'
|
102
116
|
else
|
103
117
|
"__LINE__ #{sign} #{line_diff}"
|
104
118
|
end
|
105
|
-
|
119
|
+
|
120
|
+
format(MSG_INCORRECT_LINE,
|
121
|
+
method_name: method_name,
|
122
|
+
actual: actual.source,
|
123
|
+
expected: expected)
|
124
|
+
end
|
125
|
+
|
126
|
+
def check_file(node, file_node)
|
127
|
+
return true if special_file_keyword?(file_node)
|
128
|
+
|
129
|
+
message = format(MSG_INCORRECT_FILE,
|
130
|
+
method_name: node.method_name,
|
131
|
+
expected: '__FILE__',
|
132
|
+
actual: file_node.source)
|
133
|
+
|
134
|
+
add_offense(file_node, message: message)
|
106
135
|
end
|
107
136
|
|
108
|
-
def
|
137
|
+
def check_line(node, code)
|
109
138
|
line_node = node.arguments.last
|
110
139
|
lineno_range = line_node.loc.expression
|
111
140
|
line_diff = string_first_line(code) - lineno_range.first_line
|
@@ -124,22 +153,22 @@ module RuboCop
|
|
124
153
|
end
|
125
154
|
end
|
126
155
|
|
127
|
-
def add_offense_for_same_line(
|
156
|
+
def add_offense_for_same_line(node, line_node)
|
128
157
|
return if special_line_keyword?(line_node)
|
129
158
|
|
130
159
|
add_offense(
|
131
160
|
line_node.loc.expression,
|
132
|
-
message: message_incorrect_line(line_node, nil, 0)
|
161
|
+
message: message_incorrect_line(node.method_name, line_node, nil, 0)
|
133
162
|
)
|
134
163
|
end
|
135
164
|
|
136
|
-
def add_offense_for_different_line(
|
165
|
+
def add_offense_for_different_line(node, line_node, line_diff)
|
137
166
|
sign = line_diff.positive? ? :+ : :-
|
138
167
|
return if line_with_offset?(line_node, sign, line_diff.abs)
|
139
168
|
|
140
169
|
add_offense(
|
141
170
|
line_node.loc.expression,
|
142
|
-
message: message_incorrect_line(line_node, sign, line_diff.abs)
|
171
|
+
message: message_incorrect_line(node.method_name, line_node, sign, line_diff.abs)
|
143
172
|
)
|
144
173
|
end
|
145
174
|
end
|
@@ -7,6 +7,9 @@ module RuboCop
|
|
7
7
|
# It is recommended to either always use `fdiv` or coerce one side only.
|
8
8
|
# This cop also provides other options for code consistency.
|
9
9
|
#
|
10
|
+
# This cop is marked as unsafe, because if operand variable is a string object
|
11
|
+
# then `.to_f` will be removed and an error will occur.
|
12
|
+
#
|
10
13
|
# @example EnforcedStyle: single_coerce (default)
|
11
14
|
# # bad
|
12
15
|
# a.to_f / b.to_f
|
@@ -11,6 +11,8 @@ module RuboCop
|
|
11
11
|
# The reason is that _unannotated_ format is very similar
|
12
12
|
# to encoded URLs or Date/Time formatting strings.
|
13
13
|
#
|
14
|
+
# This cop can be customized ignored methods with `IgnoredMethods`.
|
15
|
+
#
|
14
16
|
# @example EnforcedStyle: annotated (default)
|
15
17
|
#
|
16
18
|
# # bad
|
@@ -58,12 +60,18 @@ module RuboCop
|
|
58
60
|
#
|
59
61
|
# # good
|
60
62
|
# format('%06d', 10)
|
63
|
+
#
|
64
|
+
# @example IgnoredMethods: [redirect]
|
65
|
+
#
|
66
|
+
# # good
|
67
|
+
# redirect('foo/%{bar_id}')
|
68
|
+
#
|
61
69
|
class FormatStringToken < Base
|
62
70
|
include ConfigurableEnforcedStyle
|
71
|
+
include IgnoredMethods
|
63
72
|
|
64
73
|
def on_str(node)
|
65
|
-
return
|
66
|
-
return if node.each_ancestor(:xstr, :regexp).any?
|
74
|
+
return if format_string_token?(node) || use_ignored_method?(node)
|
67
75
|
|
68
76
|
detections = collect_detections(node)
|
69
77
|
return if detections.empty?
|
@@ -88,6 +96,14 @@ module RuboCop
|
|
88
96
|
}
|
89
97
|
PATTERN
|
90
98
|
|
99
|
+
def format_string_token?(node)
|
100
|
+
!node.value.include?('%') || node.each_ancestor(:xstr, :regexp).any?
|
101
|
+
end
|
102
|
+
|
103
|
+
def use_ignored_method?(node)
|
104
|
+
(parent = node.parent) && parent.send_type? && ignored_method?(parent.method_name)
|
105
|
+
end
|
106
|
+
|
91
107
|
def unannotated_format?(node, detected_style)
|
92
108
|
detected_style == :unannotated && !format_string_in_typical_context?(node)
|
93
109
|
end
|
@@ -86,9 +86,10 @@ module RuboCop
|
|
86
86
|
correct_to_elsif_from_if_inside_else_form(corrector, node, node.condition)
|
87
87
|
end
|
88
88
|
corrector.remove(range_by_whole_lines(find_end_range(node), include_final_newline: true))
|
89
|
-
|
90
|
-
|
91
|
-
)
|
89
|
+
return unless (if_branch = node.if_branch)
|
90
|
+
|
91
|
+
range = range_by_whole_lines(if_branch.source_range, include_final_newline: true)
|
92
|
+
corrector.remove(range)
|
92
93
|
end
|
93
94
|
|
94
95
|
def correct_to_elsif_from_modifier_form(corrector, node)
|
@@ -101,10 +102,12 @@ module RuboCop
|
|
101
102
|
|
102
103
|
def correct_to_elsif_from_if_inside_else_form(corrector, node, condition)
|
103
104
|
corrector.replace(node.parent.loc.else, "elsif #{condition.source}")
|
104
|
-
if_condition_range =
|
105
|
-
|
106
|
-
|
107
|
-
|
105
|
+
if_condition_range = if_condition_range(node, condition)
|
106
|
+
if (if_branch = node.if_branch)
|
107
|
+
corrector.replace(if_condition_range, if_branch.source)
|
108
|
+
else
|
109
|
+
corrector.remove(range_by_whole_lines(if_condition_range, include_final_newline: true))
|
110
|
+
end
|
108
111
|
corrector.remove(condition)
|
109
112
|
end
|
110
113
|
|
@@ -115,6 +118,10 @@ module RuboCop
|
|
115
118
|
find_end_range(node.parent)
|
116
119
|
end
|
117
120
|
|
121
|
+
def if_condition_range(node, condition)
|
122
|
+
range_between(node.loc.keyword.begin_pos, condition.source_range.end_pos)
|
123
|
+
end
|
124
|
+
|
118
125
|
def allow_if_modifier_in_else_branch?(else_branch)
|
119
126
|
allow_if_modifier? && else_branch&.modifier_form?
|
120
127
|
end
|