rubocop 1.31.0 → 1.32.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/config/default.yml +23 -0
- data/lib/rubocop/cli.rb +1 -0
- data/lib/rubocop/config.rb +1 -1
- data/lib/rubocop/config_loader_resolver.rb +1 -1
- data/lib/rubocop/cop/base.rb +1 -1
- data/lib/rubocop/cop/generator.rb +4 -0
- data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +60 -0
- data/lib/rubocop/cop/internal_affairs.rb +1 -0
- data/lib/rubocop/cop/layout/first_array_element_indentation.rb +4 -3
- data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +3 -3
- data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +57 -13
- data/lib/rubocop/cop/layout/line_continuation_spacing.rb +10 -0
- data/lib/rubocop/cop/layout/line_length.rb +2 -0
- data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +4 -1
- data/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb +45 -0
- data/lib/rubocop/cop/lint/ambiguous_block_association.rb +7 -0
- data/lib/rubocop/cop/lint/deprecated_class_methods.rb +10 -4
- data/lib/rubocop/cop/lint/literal_as_condition.rb +5 -0
- data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +60 -24
- data/lib/rubocop/cop/lint/number_conversion.rb +7 -1
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +7 -0
- data/lib/rubocop/cop/lint/require_range_parentheses.rb +57 -0
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +35 -1
- data/lib/rubocop/cop/metrics/block_length.rb +1 -0
- data/lib/rubocop/cop/metrics/method_length.rb +1 -0
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +4 -0
- data/lib/rubocop/cop/mixin/def_node.rb +2 -7
- data/lib/rubocop/cop/mixin/multiline_element_indentation.rb +12 -14
- data/lib/rubocop/cop/mixin/percent_array.rb +60 -1
- data/lib/rubocop/cop/naming/predicate_name.rb +8 -0
- data/lib/rubocop/cop/style/class_equality_comparison.rb +22 -0
- data/lib/rubocop/cop/style/empty_else.rb +37 -0
- data/lib/rubocop/cop/style/empty_heredoc.rb +59 -0
- data/lib/rubocop/cop/style/fetch_env_var.rb +10 -177
- data/lib/rubocop/cop/style/format_string_token.rb +6 -0
- data/lib/rubocop/cop/style/hash_except.rb +1 -1
- data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +2 -0
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +12 -0
- data/lib/rubocop/cop/style/module_function.rb +2 -2
- data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +9 -0
- data/lib/rubocop/cop/style/numeric_predicate.rb +20 -6
- data/lib/rubocop/cop/style/redundant_parentheses.rb +2 -1
- data/lib/rubocop/cop/style/semicolon.rb +27 -3
- data/lib/rubocop/cop/style/symbol_array.rb +2 -3
- data/lib/rubocop/cop/style/symbol_proc.rb +9 -1
- data/lib/rubocop/cop/style/trivial_accessors.rb +3 -0
- data/lib/rubocop/cop/style/word_array.rb +2 -3
- data/lib/rubocop/formatter/simple_text_formatter.rb +2 -0
- data/lib/rubocop/formatter.rb +21 -21
- data/lib/rubocop/options.rb +3 -6
- data/lib/rubocop/rake_task.rb +5 -1
- data/lib/rubocop/rspec/shared_contexts.rb +14 -14
- data/lib/rubocop/rspec/support.rb +14 -0
- data/lib/rubocop/runner.rb +4 -0
- data/lib/rubocop/server/client_command/base.rb +1 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +4 -1
- metadata +23 -5
@@ -25,30 +25,37 @@ module RuboCop
|
|
25
25
|
# to be strictly equivalent to that before the replacement.
|
26
26
|
#
|
27
27
|
# @example
|
28
|
-
# # bad
|
29
|
-
# unless
|
30
|
-
# FileUtils.
|
28
|
+
# # bad - race condition with another process may result in an error in `mkdir`
|
29
|
+
# unless Dir.exist?(path)
|
30
|
+
# FileUtils.mkdir(path)
|
31
31
|
# end
|
32
32
|
#
|
33
|
-
#
|
33
|
+
# # good - atomic and idempotent creation
|
34
|
+
# FileUtils.mkdir_p(path)
|
35
|
+
#
|
36
|
+
# # bad - race condition with another process may result in an error in `remove`
|
37
|
+
# if File.exist?(path)
|
34
38
|
# FileUtils.remove(path)
|
35
39
|
# end
|
36
40
|
#
|
37
|
-
# # good
|
38
|
-
# FileUtils.
|
39
|
-
#
|
40
|
-
# FileUtils.rm_rf(path)
|
41
|
+
# # good - atomic and idempotent removal
|
42
|
+
# FileUtils.rm_f(path)
|
41
43
|
#
|
42
44
|
class NonAtomicFileOperation < Base
|
43
45
|
extend AutoCorrector
|
44
46
|
include Alignment
|
45
47
|
include RangeHelp
|
46
48
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
MSG_REMOVE_FILE_EXIST_CHECK = 'Remove unnecessary existence check ' \
|
50
|
+
'`%<receiver>s.%<method_name>s`.'
|
51
|
+
MSG_CHANGE_FORCE_METHOD = 'Use atomic file operation method `FileUtils.%<method_name>s`.'
|
52
|
+
MAKE_FORCE_METHODS = %i[makedirs mkdir_p mkpath].freeze
|
53
|
+
MAKE_METHODS = %i[mkdir].freeze
|
54
|
+
REMOVE_FORCE_METHODS = %i[rm_f rm_rf].freeze
|
55
|
+
REMOVE_METHODS = %i[remove remove_dir remove_entry remove_entry_secure
|
56
|
+
delete unlink remove_file rm rmdir safe_unlink].freeze
|
57
|
+
RESTRICT_ON_SEND = (MAKE_METHODS + MAKE_FORCE_METHODS + REMOVE_METHODS +
|
58
|
+
REMOVE_FORCE_METHODS).freeze
|
52
59
|
|
53
60
|
# @!method send_exist_node(node)
|
54
61
|
def_node_search :send_exist_node, <<-PATTERN
|
@@ -71,50 +78,79 @@ module RuboCop
|
|
71
78
|
PATTERN
|
72
79
|
|
73
80
|
def on_send(node)
|
74
|
-
return unless node
|
81
|
+
return unless if_node_child?(node)
|
75
82
|
return if explicit_not_force?(node)
|
76
83
|
return unless (exist_node = send_exist_node(node.parent).first)
|
77
84
|
return unless exist_node.first_argument == node.first_argument
|
78
85
|
|
79
|
-
|
86
|
+
register_offense(node, exist_node)
|
80
87
|
end
|
81
88
|
|
82
89
|
private
|
83
90
|
|
84
|
-
def
|
91
|
+
def if_node_child?(node)
|
92
|
+
return false unless (parent = node.parent)
|
93
|
+
|
94
|
+
parent.if_type? && !allowable_use_with_if?(parent)
|
95
|
+
end
|
96
|
+
|
97
|
+
def allowable_use_with_if?(if_node)
|
98
|
+
if_node.condition.and_type? || if_node.condition.or_type? || if_node.else_branch
|
99
|
+
end
|
100
|
+
|
101
|
+
def register_offense(node, exist_node)
|
102
|
+
unless force_method?(node)
|
103
|
+
add_offense(node,
|
104
|
+
message: format(MSG_CHANGE_FORCE_METHOD,
|
105
|
+
method_name: replacement_method(node)))
|
106
|
+
end
|
107
|
+
|
85
108
|
range = range_between(node.parent.loc.keyword.begin_pos,
|
86
109
|
exist_node.loc.expression.end_pos)
|
87
|
-
|
88
|
-
add_offense(range, message: message(exist_node)) do |corrector|
|
110
|
+
add_offense(range, message: message_remove_file_exist_check(exist_node)) do |corrector|
|
89
111
|
autocorrect(corrector, node, range)
|
90
112
|
end
|
91
113
|
end
|
92
114
|
|
93
|
-
def
|
115
|
+
def message_remove_file_exist_check(node)
|
94
116
|
receiver, method_name = receiver_and_method_name(node)
|
95
|
-
format(
|
117
|
+
format(MSG_REMOVE_FILE_EXIST_CHECK, receiver: receiver, method_name: method_name)
|
96
118
|
end
|
97
119
|
|
98
120
|
def autocorrect(corrector, node, range)
|
99
121
|
corrector.remove(range)
|
122
|
+
autocorrect_replace_method(corrector, node)
|
123
|
+
corrector.remove(node.parent.loc.end) if node.parent.multiline?
|
124
|
+
end
|
125
|
+
|
126
|
+
def autocorrect_replace_method(corrector, node)
|
127
|
+
return if force_method?(node)
|
128
|
+
|
100
129
|
corrector.replace(node.child_nodes.first.loc.name, 'FileUtils')
|
101
130
|
corrector.replace(node.loc.selector, replacement_method(node))
|
102
|
-
corrector.remove(node.parent.loc.end) if node.parent.multiline?
|
103
131
|
end
|
104
132
|
|
105
133
|
def replacement_method(node)
|
106
|
-
return node.method_name if force_option?(node)
|
107
|
-
|
108
134
|
if MAKE_METHODS.include?(node.method_name)
|
109
135
|
'mkdir_p'
|
110
136
|
elsif REMOVE_METHODS.include?(node.method_name)
|
111
|
-
'
|
137
|
+
'rm_f'
|
138
|
+
else
|
139
|
+
node.method_name
|
112
140
|
end
|
113
141
|
end
|
114
142
|
|
143
|
+
def force_method?(node)
|
144
|
+
force_method_name?(node) || force_option?(node)
|
145
|
+
end
|
146
|
+
|
115
147
|
def force_option?(node)
|
116
148
|
node.arguments.any? { |arg| force?(arg) }
|
117
149
|
end
|
150
|
+
|
151
|
+
def force_method_name?(node)
|
152
|
+
(MAKE_FORCE_METHODS + REMOVE_FORCE_METHODS).include?(node.method_name)
|
153
|
+
end
|
118
154
|
end
|
119
155
|
end
|
120
156
|
end
|
@@ -16,7 +16,8 @@ module RuboCop
|
|
16
16
|
# NOTE: Some values cannot be converted properly using one of the `Kernel`
|
17
17
|
# method (for instance, `Time` and `DateTime` values are allowed by this
|
18
18
|
# cop by default). Similarly, Rails' duration methods do not work well
|
19
|
-
# with `Integer()` and can be ignored with `IgnoredMethods`.
|
19
|
+
# with `Integer()` and can be ignored with `IgnoredMethods`. By default,
|
20
|
+
# there are no methods to ignored.
|
20
21
|
#
|
21
22
|
# @safety
|
22
23
|
# Autocorrection is unsafe because it is not guaranteed that the
|
@@ -45,6 +46,11 @@ module RuboCop
|
|
45
46
|
# foo.try { |i| Float(i) }
|
46
47
|
# bar.send { |i| Complex(i) }
|
47
48
|
#
|
49
|
+
# @example IgnoredMethods: [] (default)
|
50
|
+
#
|
51
|
+
# # bad
|
52
|
+
# 10.minutes.to_i
|
53
|
+
#
|
48
54
|
# @example IgnoredMethods: [minutes]
|
49
55
|
#
|
50
56
|
# # good
|
@@ -35,6 +35,13 @@ module RuboCop
|
|
35
35
|
# # good - without `&.` this will always return `true`
|
36
36
|
# foo&.respond_to?(:to_a)
|
37
37
|
#
|
38
|
+
# @example AllowedMethods: [foo?]
|
39
|
+
# # bad
|
40
|
+
# do_something if attrs&.foo?(:[])
|
41
|
+
#
|
42
|
+
# # good
|
43
|
+
# do_something if attrs&.bar?(:[])
|
44
|
+
#
|
38
45
|
class RedundantSafeNavigation < Base
|
39
46
|
include AllowedMethods
|
40
47
|
include RangeHelp
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# Checks that a range literal is enclosed in parentheses when the end of the range is
|
7
|
+
# at a line break.
|
8
|
+
#
|
9
|
+
# NOTE: The following is maybe intended for `(42..)`. But, compatible is `42..do_something`.
|
10
|
+
# So, this cop does not provide autocorrection because it is left to user.
|
11
|
+
#
|
12
|
+
# [source,ruby]
|
13
|
+
# ----
|
14
|
+
# case condition
|
15
|
+
# when 42..
|
16
|
+
# do_something
|
17
|
+
# end
|
18
|
+
# ----
|
19
|
+
#
|
20
|
+
# @example
|
21
|
+
#
|
22
|
+
# # bad - Represents `(1..42)`, not endless range.
|
23
|
+
# 1..
|
24
|
+
# 42
|
25
|
+
#
|
26
|
+
# # good - It's incompatible, but your intentions when using endless range may be:
|
27
|
+
# (1..)
|
28
|
+
# 42
|
29
|
+
#
|
30
|
+
# # good
|
31
|
+
# 1..42
|
32
|
+
#
|
33
|
+
# # good
|
34
|
+
# (1..42)
|
35
|
+
#
|
36
|
+
# # good
|
37
|
+
# (1..
|
38
|
+
# 42)
|
39
|
+
#
|
40
|
+
class RequireRangeParentheses < Base
|
41
|
+
MSG = 'Wrap the endless range literal `%<range>s` to avoid precedence ambiguity.'
|
42
|
+
|
43
|
+
def on_irange(node)
|
44
|
+
return if node.parent&.begin_type?
|
45
|
+
return unless node.begin && node.end
|
46
|
+
return if same_line?(node.begin, node.end)
|
47
|
+
|
48
|
+
message = format(MSG, range: "#{node.begin.source}#{node.loc.operator.source}")
|
49
|
+
|
50
|
+
add_offense(node, message: message)
|
51
|
+
end
|
52
|
+
|
53
|
+
alias on_erange on_irange
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -25,6 +25,7 @@ module RuboCop
|
|
25
25
|
# x&.foo || bar
|
26
26
|
class SafeNavigationChain < Base
|
27
27
|
include NilMethods
|
28
|
+
extend AutoCorrector
|
28
29
|
extend TargetRubyVersion
|
29
30
|
|
30
31
|
minimum_target_ruby_version 2.3
|
@@ -48,12 +49,45 @@ module RuboCop
|
|
48
49
|
Parser::Source::Range.new(node.source_range.source_buffer,
|
49
50
|
safe_nav.source_range.end_pos,
|
50
51
|
method_chain.source_range.end_pos)
|
51
|
-
add_offense(location)
|
52
|
+
add_offense(location) do |corrector|
|
53
|
+
autocorrect(corrector, offense_range: location, send_node: method_chain)
|
54
|
+
end
|
52
55
|
end
|
53
56
|
end
|
54
57
|
|
55
58
|
private
|
56
59
|
|
60
|
+
# @param [Parser::Source::Range] offense_range
|
61
|
+
# @param [RuboCop::AST::SendNode] send_node
|
62
|
+
# @return [String]
|
63
|
+
def add_safe_navigation_operator(offense_range:, send_node:)
|
64
|
+
source = \
|
65
|
+
if send_node.method?(:[]) || send_node.method?(:[]=)
|
66
|
+
format(
|
67
|
+
'%<method_name>s(%<arguments>s)',
|
68
|
+
arguments: send_node.arguments.map(&:source).join(', '),
|
69
|
+
method_name: send_node.method_name
|
70
|
+
)
|
71
|
+
else
|
72
|
+
offense_range.source.dup
|
73
|
+
end
|
74
|
+
source.prepend('.') unless send_node.dot?
|
75
|
+
source.prepend('&')
|
76
|
+
end
|
77
|
+
|
78
|
+
# @param [RuboCop::Cop::Corrector] corrector
|
79
|
+
# @param [Parser::Source::Range] offense_range
|
80
|
+
# @param [RuboCop::AST::SendNode] send_node
|
81
|
+
def autocorrect(corrector, offense_range:, send_node:)
|
82
|
+
corrector.replace(
|
83
|
+
offense_range,
|
84
|
+
add_safe_navigation_operator(
|
85
|
+
offense_range: offense_range,
|
86
|
+
send_node: send_node
|
87
|
+
)
|
88
|
+
)
|
89
|
+
end
|
90
|
+
|
57
91
|
def method_chain(node)
|
58
92
|
chain = node
|
59
93
|
chain = chain.parent if chain.send_type? && chain.parent&.call_type?
|
@@ -15,6 +15,7 @@ module RuboCop
|
|
15
15
|
#
|
16
16
|
# NOTE: The `ExcludedMethods` configuration is deprecated and only kept
|
17
17
|
# for backwards compatibility. Please use `IgnoredMethods` instead.
|
18
|
+
# By default, there are no methods to ignored.
|
18
19
|
#
|
19
20
|
# @example CountAsOne: ['array', 'heredoc']
|
20
21
|
#
|
@@ -13,6 +13,7 @@ module RuboCop
|
|
13
13
|
#
|
14
14
|
# NOTE: The `ExcludedMethods` configuration is deprecated and only kept
|
15
15
|
# for backwards compatibility. Please use `IgnoredMethods` instead.
|
16
|
+
# By default, there are no methods to ignored.
|
16
17
|
#
|
17
18
|
# @example CountAsOne: ['array', 'heredoc']
|
18
19
|
#
|
@@ -46,6 +46,8 @@ module RuboCop
|
|
46
46
|
if node.send_type?
|
47
47
|
args = process_args(node.arguments)
|
48
48
|
return extract_breakable_node_from_elements(node, args, max)
|
49
|
+
elsif node.def_type?
|
50
|
+
return extract_breakable_node_from_elements(node, node.arguments, max)
|
49
51
|
elsif node.array_type? || node.hash_type?
|
50
52
|
return extract_breakable_node_from_elements(node, node.children, max)
|
51
53
|
end
|
@@ -216,6 +218,8 @@ module RuboCop
|
|
216
218
|
|
217
219
|
# @api private
|
218
220
|
def already_on_multiple_lines?(node)
|
221
|
+
return node.first_line != node.arguments.last.last_line if node.def_type?
|
222
|
+
|
219
223
|
node.first_line != node.last_line
|
220
224
|
end
|
221
225
|
end
|
@@ -5,8 +5,7 @@ module RuboCop
|
|
5
5
|
# Common functionality for checking def nodes.
|
6
6
|
module DefNode
|
7
7
|
extend NodePattern::Macros
|
8
|
-
|
9
|
-
NON_PUBLIC_MODIFIERS = %w[private protected].freeze
|
8
|
+
include VisibilityHelp
|
10
9
|
|
11
10
|
private
|
12
11
|
|
@@ -15,11 +14,7 @@ module RuboCop
|
|
15
14
|
end
|
16
15
|
|
17
16
|
def preceding_non_public_modifier?(node)
|
18
|
-
|
19
|
-
end
|
20
|
-
|
21
|
-
def stripped_source_upto(index)
|
22
|
-
processed_source[0..index].map(&:strip)
|
17
|
+
node_visibility(node) != :public
|
23
18
|
end
|
24
19
|
|
25
20
|
# @!method non_public_modifier?(node)
|
@@ -26,7 +26,7 @@ module RuboCop
|
|
26
26
|
def check_first(first, left_brace, left_parenthesis, offset)
|
27
27
|
actual_column = first.source_range.column
|
28
28
|
|
29
|
-
indent_base_column, indent_base_type = indent_base(left_brace, left_parenthesis)
|
29
|
+
indent_base_column, indent_base_type = indent_base(left_brace, first, left_parenthesis)
|
30
30
|
expected_column = indent_base_column + configured_indentation_width + offset
|
31
31
|
|
32
32
|
@column_delta = expected_column - actual_column
|
@@ -47,11 +47,12 @@ module RuboCop
|
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
|
-
def indent_base(left_brace, left_parenthesis)
|
50
|
+
def indent_base(left_brace, first, left_parenthesis)
|
51
51
|
return [left_brace.column, :left_brace_or_bracket] if style == brace_alignment_style
|
52
52
|
|
53
|
-
pair = hash_pair_where_value_beginning_with(left_brace)
|
54
|
-
if pair && key_and_value_begin_on_same_line?(pair) &&
|
53
|
+
pair = hash_pair_where_value_beginning_with(left_brace, first)
|
54
|
+
if pair && key_and_value_begin_on_same_line?(pair) &&
|
55
|
+
right_sibling_begins_on_subsequent_line?(pair)
|
55
56
|
return [pair.loc.column, :parent_hash_key]
|
56
57
|
end
|
57
58
|
|
@@ -62,23 +63,20 @@ module RuboCop
|
|
62
63
|
[left_brace.source_line =~ /\S/, :start_of_line]
|
63
64
|
end
|
64
65
|
|
65
|
-
def hash_pair_where_value_beginning_with(left_brace)
|
66
|
-
|
67
|
-
node.parent&.pair_type? ? node.parent : nil
|
68
|
-
end
|
66
|
+
def hash_pair_where_value_beginning_with(left_brace, first)
|
67
|
+
return unless first && first.parent.loc.begin == left_brace
|
69
68
|
|
70
|
-
|
71
|
-
processed_source.ast.each_descendant do |node|
|
72
|
-
if node.loc.is_a?(Parser::Source::Map::Collection) && (node.loc.begin == left_brace)
|
73
|
-
break node
|
74
|
-
end
|
75
|
-
end
|
69
|
+
first.parent&.parent&.pair_type? ? first.parent.parent : nil
|
76
70
|
end
|
77
71
|
|
78
72
|
def key_and_value_begin_on_same_line?(pair)
|
79
73
|
same_line?(pair.key, pair.value)
|
80
74
|
end
|
81
75
|
|
76
|
+
def right_sibling_begins_on_subsequent_line?(pair)
|
77
|
+
pair.right_sibling && (pair.last_line < pair.right_sibling.first_line)
|
78
|
+
end
|
79
|
+
|
82
80
|
def detected_styles(actual_column, offset, left_parenthesis, left_brace)
|
83
81
|
base_column = actual_column - configured_indentation_width - offset
|
84
82
|
detected_styles_for_column(base_column, left_parenthesis, left_brace)
|
@@ -44,13 +44,26 @@ module RuboCop
|
|
44
44
|
no_acceptable_style! if brackets_required
|
45
45
|
|
46
46
|
bracketed_array = build_bracketed_array(node)
|
47
|
-
message =
|
47
|
+
message = build_message_for_bracketed_array(bracketed_array)
|
48
48
|
|
49
49
|
add_offense(node, message: message) do |corrector|
|
50
50
|
corrector.replace(node, bracketed_array)
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
+
# @param [String] preferred_array_code
|
55
|
+
# @return [String]
|
56
|
+
def build_message_for_bracketed_array(preferred_array_code)
|
57
|
+
format(
|
58
|
+
self.class::ARRAY_MSG,
|
59
|
+
prefer: if preferred_array_code.include?("\n")
|
60
|
+
'an array literal `[...]`'
|
61
|
+
else
|
62
|
+
"`#{preferred_array_code}`"
|
63
|
+
end
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
54
67
|
def check_bracketed_array(node, literal_prefix)
|
55
68
|
return if allowed_bracket_array?(node)
|
56
69
|
|
@@ -63,6 +76,52 @@ module RuboCop
|
|
63
76
|
percent_literal_corrector.correct(corrector, node, literal_prefix)
|
64
77
|
end
|
65
78
|
end
|
79
|
+
|
80
|
+
# @param [RuboCop::AST::ArrayNode] node
|
81
|
+
# @param [Array<String>] elements
|
82
|
+
# @return [String]
|
83
|
+
def build_bracketed_array_with_appropriate_whitespace(elements:, node:)
|
84
|
+
[
|
85
|
+
'[',
|
86
|
+
whitespace_leading(node),
|
87
|
+
elements.join(",#{whitespace_between(node)}"),
|
88
|
+
whitespace_trailing(node),
|
89
|
+
']'
|
90
|
+
].join
|
91
|
+
end
|
92
|
+
|
93
|
+
# Provides whitespace between elements for building a bracketed array.
|
94
|
+
# %w[ a b c ]
|
95
|
+
# ^^^
|
96
|
+
# @param [RuboCop::AST::ArrayNode] node
|
97
|
+
# @return [String]
|
98
|
+
def whitespace_between(node)
|
99
|
+
if node.children.length >= 2
|
100
|
+
node.source[
|
101
|
+
node.children[0].loc.expression.end_pos...node.children[1].loc.expression.begin_pos
|
102
|
+
]
|
103
|
+
else
|
104
|
+
' '
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Provides leading whitespace for building a bracketed array.
|
109
|
+
# %w[ a b c ]
|
110
|
+
# ^^
|
111
|
+
# @param [RuboCop::AST::ArrayNode] node
|
112
|
+
# @return [String]
|
113
|
+
def whitespace_leading(node)
|
114
|
+
node.source[node.loc.begin.end_pos...node.children[0].loc.expression.begin_pos]
|
115
|
+
end
|
116
|
+
|
117
|
+
# Provides trailing whitespace for building a bracketed array.
|
118
|
+
# %w[ a b c ]
|
119
|
+
# ^^^^
|
120
|
+
# @param [RuboCop::AST::ArrayNode] node
|
121
|
+
# @return [String]
|
122
|
+
def whitespace_trailing(node)
|
123
|
+
node.source[node.children[-1].loc.expression.end_pos...node.loc.end.begin_pos]
|
124
|
+
end
|
66
125
|
end
|
67
126
|
end
|
68
127
|
end
|
@@ -4,6 +4,8 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module Naming
|
6
6
|
# Makes sure that predicates are named properly.
|
7
|
+
# `is_a?` method is allowed by default.
|
8
|
+
# These are customizable with `AllowedMethods` option.
|
7
9
|
#
|
8
10
|
# @example
|
9
11
|
# # bad
|
@@ -27,6 +29,12 @@ module RuboCop
|
|
27
29
|
# # good
|
28
30
|
# def value?
|
29
31
|
# end
|
32
|
+
#
|
33
|
+
# @example AllowedMethods: ['is_a?'] (default)
|
34
|
+
# # good
|
35
|
+
# def is_a?(value)
|
36
|
+
# end
|
37
|
+
#
|
30
38
|
class PredicateName < Base
|
31
39
|
include AllowedMethods
|
32
40
|
|
@@ -5,6 +5,8 @@ module RuboCop
|
|
5
5
|
module Style
|
6
6
|
# Enforces the use of `Object#instance_of?` instead of class comparison
|
7
7
|
# for equality.
|
8
|
+
# `==`, `equal?`, and `eql?` methods are ignored by default.
|
9
|
+
# These are customizable with `IgnoredMethods` option.
|
8
10
|
#
|
9
11
|
# @example
|
10
12
|
# # bad
|
@@ -16,6 +18,26 @@ module RuboCop
|
|
16
18
|
# # good
|
17
19
|
# var.instance_of?(Date)
|
18
20
|
#
|
21
|
+
# @example IgnoreMethods: [] (default)
|
22
|
+
# # good
|
23
|
+
# var.instance_of?(Date)
|
24
|
+
#
|
25
|
+
# # bad
|
26
|
+
# var.class == Date
|
27
|
+
# var.class.equal?(Date)
|
28
|
+
# var.class.eql?(Date)
|
29
|
+
# var.class.name == 'Date'
|
30
|
+
#
|
31
|
+
# @example IgnoreMethods: [`==`]
|
32
|
+
# # good
|
33
|
+
# var.instance_of?(Date)
|
34
|
+
# var.class == Date
|
35
|
+
# var.class.name == 'Date'
|
36
|
+
#
|
37
|
+
# # bad
|
38
|
+
# var.class.equal?(Date)
|
39
|
+
# var.class.eql?(Date)
|
40
|
+
#
|
19
41
|
class ClassEqualityComparison < Base
|
20
42
|
include RangeHelp
|
21
43
|
include IgnoredMethods
|
@@ -89,6 +89,41 @@ module RuboCop
|
|
89
89
|
# if condition
|
90
90
|
# statement
|
91
91
|
# end
|
92
|
+
#
|
93
|
+
# @example AllowComments: false (default)
|
94
|
+
#
|
95
|
+
# # bad
|
96
|
+
# if condition
|
97
|
+
# statement
|
98
|
+
# else
|
99
|
+
# # something comment
|
100
|
+
# nil
|
101
|
+
# end
|
102
|
+
#
|
103
|
+
# # bad
|
104
|
+
# if condition
|
105
|
+
# statement
|
106
|
+
# else
|
107
|
+
# # something comment
|
108
|
+
# end
|
109
|
+
#
|
110
|
+
# @example AllowComments: true
|
111
|
+
#
|
112
|
+
# # good
|
113
|
+
# if condition
|
114
|
+
# statement
|
115
|
+
# else
|
116
|
+
# # something comment
|
117
|
+
# nil
|
118
|
+
# end
|
119
|
+
#
|
120
|
+
# # good
|
121
|
+
# if condition
|
122
|
+
# statement
|
123
|
+
# else
|
124
|
+
# # something comment
|
125
|
+
# end
|
126
|
+
#
|
92
127
|
class EmptyElse < Base
|
93
128
|
include OnNormalIfUnless
|
94
129
|
include ConfigurableEnforcedStyle
|
@@ -108,6 +143,8 @@ module RuboCop
|
|
108
143
|
private
|
109
144
|
|
110
145
|
def check(node)
|
146
|
+
return if cop_config['AllowComments'] && comment_in_else?(node.loc)
|
147
|
+
|
111
148
|
empty_check(node) if empty_style?
|
112
149
|
nil_check(node) if nil_style?
|
113
150
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Checks for using empty heredoc to reduce redundancy.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
#
|
10
|
+
# # bad
|
11
|
+
# <<~EOS
|
12
|
+
# EOS
|
13
|
+
#
|
14
|
+
# <<-EOS
|
15
|
+
# EOS
|
16
|
+
#
|
17
|
+
# <<EOS
|
18
|
+
# EOS
|
19
|
+
#
|
20
|
+
# # good
|
21
|
+
# ''
|
22
|
+
#
|
23
|
+
# # bad
|
24
|
+
# do_something(<<~EOS)
|
25
|
+
# EOS
|
26
|
+
#
|
27
|
+
# do_something(<<-EOS)
|
28
|
+
# EOS
|
29
|
+
#
|
30
|
+
# do_something(<<EOS)
|
31
|
+
# EOS
|
32
|
+
#
|
33
|
+
# # good
|
34
|
+
# do_something('')
|
35
|
+
#
|
36
|
+
class EmptyHeredoc < Base
|
37
|
+
include Heredoc
|
38
|
+
include RangeHelp
|
39
|
+
extend AutoCorrector
|
40
|
+
|
41
|
+
MSG = 'Use an empty string literal instead of heredoc.'
|
42
|
+
|
43
|
+
def on_heredoc(node)
|
44
|
+
heredoc_body = node.loc.heredoc_body
|
45
|
+
|
46
|
+
return unless heredoc_body.source.empty?
|
47
|
+
|
48
|
+
add_offense(node) do |corrector|
|
49
|
+
heredoc_end = node.loc.heredoc_end
|
50
|
+
|
51
|
+
corrector.replace(node, "''")
|
52
|
+
corrector.remove(range_by_whole_lines(heredoc_body, include_final_newline: true))
|
53
|
+
corrector.remove(range_by_whole_lines(heredoc_end, include_final_newline: true))
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|