rubocop 1.44.0 → 1.45.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 +16 -31
- data/lib/rubocop/cli.rb +54 -8
- data/lib/rubocop/config_loader_resolver.rb +3 -4
- data/lib/rubocop/cop/base.rb +27 -9
- data/lib/rubocop/cop/commissioner.rb +8 -2
- data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/class_structure.rb +2 -16
- data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_operators.rb +1 -1
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +11 -11
- data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +4 -4
- data/lib/rubocop/cop/layout/space_inside_string_interpolation.rb +5 -4
- data/lib/rubocop/cop/lint/ambiguous_operator.rb +4 -0
- data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +4 -0
- data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +15 -17
- data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +1 -1
- data/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb +1 -0
- data/lib/rubocop/cop/lint/nested_method_definition.rb +8 -5
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +7 -4
- data/lib/rubocop/cop/lint/void.rb +2 -0
- data/lib/rubocop/cop/metrics/block_length.rb +1 -1
- data/lib/rubocop/cop/mixin/allowed_methods.rb +3 -1
- data/lib/rubocop/cop/mixin/comments_help.rb +5 -3
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +13 -9
- data/lib/rubocop/cop/mixin/surrounding_space.rb +3 -3
- data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
- data/lib/rubocop/cop/naming/block_forwarding.rb +4 -0
- data/lib/rubocop/cop/naming/class_and_module_camel_case.rb +1 -1
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +8 -1
- data/lib/rubocop/cop/style/arguments_forwarding.rb +1 -0
- data/lib/rubocop/cop/style/class_and_module_children.rb +1 -1
- data/lib/rubocop/cop/style/command_literal.rb +1 -1
- data/lib/rubocop/cop/style/documentation.rb +1 -1
- data/lib/rubocop/cop/style/documentation_method.rb +6 -0
- data/lib/rubocop/cop/style/multiline_ternary_operator.rb +18 -3
- data/lib/rubocop/cop/style/numbered_parameters_limit.rb +11 -3
- data/lib/rubocop/cop/style/one_line_conditional.rb +1 -1
- data/lib/rubocop/cop/style/operator_method_call.rb +1 -1
- data/lib/rubocop/cop/style/parallel_assignment.rb +3 -1
- data/lib/rubocop/cop/style/redundant_condition.rb +16 -1
- data/lib/rubocop/cop/style/redundant_heredoc_delimiter_quotes.rb +58 -0
- data/lib/rubocop/cop/style/self_assignment.rb +2 -2
- data/lib/rubocop/cop/style/symbol_array.rb +1 -1
- data/lib/rubocop/cop/style/word_array.rb +1 -1
- data/lib/rubocop/cop/style/yoda_condition.rb +12 -5
- data/lib/rubocop/cop/style/yoda_expression.rb +11 -2
- data/lib/rubocop/cop/team.rb +19 -14
- data/lib/rubocop/cop/variable_force/scope.rb +3 -3
- data/lib/rubocop/cop/variable_force/variable_table.rb +3 -1
- data/lib/rubocop/options.rb +22 -1
- data/lib/rubocop/rspec/expect_offense.rb +1 -1
- data/lib/rubocop/runner.rb +40 -4
- data/lib/rubocop/server/cache.rb +7 -2
- data/lib/rubocop/server/cli.rb +37 -18
- data/lib/rubocop/server/client_command/exec.rb +1 -1
- data/lib/rubocop/server/client_command/start.rb +6 -1
- data/lib/rubocop/server/core.rb +23 -8
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +1 -0
- metadata +8 -27
@@ -8,27 +8,25 @@ module RuboCop
|
|
8
8
|
#
|
9
9
|
# @example
|
10
10
|
# # bad
|
11
|
+
# <<-SQL
|
12
|
+
# bar
|
13
|
+
# SQL
|
14
|
+
# .strip_indent
|
11
15
|
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
# <<-SQL
|
18
|
-
# bar
|
19
|
-
# SQL
|
20
|
-
# .strip_indent
|
21
|
-
# .trim
|
16
|
+
# <<-SQL
|
17
|
+
# bar
|
18
|
+
# SQL
|
19
|
+
# .strip_indent
|
20
|
+
# .trim
|
22
21
|
#
|
23
22
|
# # good
|
23
|
+
# <<~SQL
|
24
|
+
# bar
|
25
|
+
# SQL
|
24
26
|
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
# <<~SQL.trim
|
30
|
-
# bar
|
31
|
-
# SQL
|
27
|
+
# <<~SQL.trim
|
28
|
+
# bar
|
29
|
+
# SQL
|
32
30
|
#
|
33
31
|
class HeredocMethodCallPosition < Base
|
34
32
|
include RangeHelp
|
@@ -108,7 +108,7 @@ module RuboCop
|
|
108
108
|
return unless def_ancestor
|
109
109
|
|
110
110
|
within_scoping_def =
|
111
|
-
node.each_ancestor(:block, :sclass).any? do |ancestor|
|
111
|
+
node.each_ancestor(:block, :numblock, :sclass).any? do |ancestor|
|
112
112
|
scoping_method_call?(ancestor)
|
113
113
|
end
|
114
114
|
|
@@ -120,7 +120,7 @@ module RuboCop
|
|
120
120
|
|
121
121
|
def scoping_method_call?(child)
|
122
122
|
child.sclass_type? || eval_call?(child) || exec_call?(child) ||
|
123
|
-
|
123
|
+
class_constructor?(child) || allowed_method_name?(child)
|
124
124
|
end
|
125
125
|
|
126
126
|
def allowed_method_name?(node)
|
@@ -139,9 +139,12 @@ module RuboCop
|
|
139
139
|
(block (send _ {:instance_exec :class_exec :module_exec} ...) ...)
|
140
140
|
PATTERN
|
141
141
|
|
142
|
-
# @!method
|
143
|
-
def_node_matcher :
|
144
|
-
(block
|
142
|
+
# @!method class_constructor?(node)
|
143
|
+
def_node_matcher :class_constructor?, <<~PATTERN
|
144
|
+
({block numblock} {
|
145
|
+
(send (const {nil? cbase} {:Class :Module :Struct}) :new ...)
|
146
|
+
(send (const {nil? cbase} :Data) :define ...)
|
147
|
+
} ...)
|
145
148
|
PATTERN
|
146
149
|
end
|
147
150
|
end
|
@@ -167,9 +167,12 @@ module RuboCop
|
|
167
167
|
({block numblock} (send _ {:class_eval :instance_eval}) ...)
|
168
168
|
PATTERN
|
169
169
|
|
170
|
-
# @!method
|
171
|
-
def_node_matcher :
|
172
|
-
({block numblock}
|
170
|
+
# @!method class_constructor?(node)
|
171
|
+
def_node_matcher :class_constructor?, <<~PATTERN
|
172
|
+
({block numblock} {
|
173
|
+
(send (const {nil? cbase} {:Class :Module :Struct}) :new ...)
|
174
|
+
(send (const {nil? cbase} :Data) :define ...)
|
175
|
+
} ...)
|
173
176
|
PATTERN
|
174
177
|
|
175
178
|
def check_node(node)
|
@@ -270,7 +273,7 @@ module RuboCop
|
|
270
273
|
|
271
274
|
def eval_call?(child)
|
272
275
|
class_or_instance_eval?(child) ||
|
273
|
-
|
276
|
+
class_constructor?(child) ||
|
274
277
|
any_context_creating_methods?(child)
|
275
278
|
end
|
276
279
|
|
@@ -51,7 +51,7 @@ module RuboCop
|
|
51
51
|
def on_block(node)
|
52
52
|
return if allowed_method?(node.method_name) || matches_allowed_pattern?(node.method_name)
|
53
53
|
return if method_receiver_excluded?(node)
|
54
|
-
return if node.class_constructor?
|
54
|
+
return if node.class_constructor?
|
55
55
|
|
56
56
|
check_code_length(node)
|
57
57
|
end
|
@@ -3,7 +3,9 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
# This module encapsulates the ability to allow certain methods when
|
6
|
-
# parsing.
|
6
|
+
# parsing. Even if the code is in offense, if it contains methods
|
7
|
+
# that are allowed. This module is equivalent to the IgnoredMethods module,
|
8
|
+
# which will be deprecated in RuboCop 2.0.
|
7
9
|
module AllowedMethods
|
8
10
|
private
|
9
11
|
|
@@ -62,10 +62,12 @@ module RuboCop
|
|
62
62
|
# Returns the end line of a node, which might be a comment and not part of the AST
|
63
63
|
# End line is considered either the line at which another node starts, or
|
64
64
|
# the line at which the parent node ends.
|
65
|
-
# rubocop:disable Metrics/AbcSize
|
65
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
66
66
|
def find_end_line(node)
|
67
|
-
if node.if_type? && node.
|
67
|
+
if node.if_type? && node.else?
|
68
68
|
node.loc.else.line
|
69
|
+
elsif node.if_type? && node.ternary?
|
70
|
+
node.else_branch.loc.line
|
69
71
|
elsif (next_sibling = node.right_sibling)
|
70
72
|
next_sibling.loc.line
|
71
73
|
elsif (parent = node.parent)
|
@@ -74,7 +76,7 @@ module RuboCop
|
|
74
76
|
node.loc.end.line
|
75
77
|
end
|
76
78
|
end
|
77
|
-
# rubocop:enable Metrics/AbcSize
|
79
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
78
80
|
end
|
79
81
|
end
|
80
82
|
end
|
@@ -95,16 +95,19 @@ module RuboCop
|
|
95
95
|
use_modifier_form_without_parenthesized_method_call?(method_dispatch_node)
|
96
96
|
end
|
97
97
|
|
98
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
98
99
|
def def_node_that_require_parentheses(node)
|
99
100
|
last_pair = node.parent.pairs.last
|
100
101
|
return unless last_pair.key.source == last_pair.value.source
|
101
|
-
return unless (
|
102
|
-
return
|
102
|
+
return unless (dispatch_node = find_ancestor_method_dispatch_node(node))
|
103
|
+
return if node.respond_to?(:parenthesized?) && !node.parenthesized?
|
104
|
+
return unless last_expression?(dispatch_node) || method_dispatch_as_argument?(dispatch_node)
|
103
105
|
|
104
106
|
def_node = node.each_ancestor(:send, :csend, :super, :yield).first
|
105
107
|
|
106
108
|
DefNode.new(def_node) unless def_node && def_node.arguments.empty?
|
107
109
|
end
|
110
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
108
111
|
|
109
112
|
def find_ancestor_method_dispatch_node(node)
|
110
113
|
return unless (ancestor = node.parent.parent)
|
@@ -129,15 +132,16 @@ module RuboCop
|
|
129
132
|
ancestor.ancestors.any? { |node| node.respond_to?(:modifier_form?) && node.modifier_form? }
|
130
133
|
end
|
131
134
|
|
132
|
-
def
|
133
|
-
|
135
|
+
def last_expression?(ancestor)
|
136
|
+
ancestor.right_sibling ||
|
137
|
+
ancestor.each_ancestor.find { |node| node.assignment? || node.send_type? }&.right_sibling
|
138
|
+
end
|
134
139
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
end&.right_sibling
|
140
|
+
def method_dispatch_as_argument?(method_dispatch_node)
|
141
|
+
parent = method_dispatch_node.parent
|
142
|
+
return false unless parent
|
139
143
|
|
140
|
-
|
144
|
+
parent.call_type? || parent.super_type? || parent.yield_type?
|
141
145
|
end
|
142
146
|
|
143
147
|
def breakdown_value_types_of_hash(hash_node)
|
@@ -107,9 +107,9 @@ module RuboCop
|
|
107
107
|
end
|
108
108
|
end
|
109
109
|
|
110
|
-
def empty_brackets?(left_bracket_token, right_bracket_token)
|
111
|
-
left_index =
|
112
|
-
right_index =
|
110
|
+
def empty_brackets?(left_bracket_token, right_bracket_token, tokens: processed_source.tokens)
|
111
|
+
left_index = tokens.index(left_bracket_token)
|
112
|
+
right_index = tokens.index(right_bracket_token)
|
113
113
|
right_index && left_index == right_index - 1
|
114
114
|
end
|
115
115
|
|
@@ -132,7 +132,7 @@ module RuboCop
|
|
132
132
|
|
133
133
|
def avoid_comma(kind, comma_begin_pos, extra_info)
|
134
134
|
range = range_between(comma_begin_pos, comma_begin_pos + 1)
|
135
|
-
article =
|
135
|
+
article = kind.include?('array') ? 'an' : 'a'
|
136
136
|
msg = format(
|
137
137
|
MSG,
|
138
138
|
command: 'Avoid',
|
@@ -129,7 +129,8 @@ module RuboCop
|
|
129
129
|
end
|
130
130
|
|
131
131
|
def offense?(node)
|
132
|
-
(group_style? && access_modifier_is_inlined?(node)
|
132
|
+
(group_style? && access_modifier_is_inlined?(node) &&
|
133
|
+
!right_siblings_same_inline_method?(node)) ||
|
133
134
|
(inline_style? && access_modifier_is_not_inlined?(node))
|
134
135
|
end
|
135
136
|
|
@@ -149,6 +150,12 @@ module RuboCop
|
|
149
150
|
!access_modifier_is_inlined?(node)
|
150
151
|
end
|
151
152
|
|
153
|
+
def right_siblings_same_inline_method?(node)
|
154
|
+
node.right_siblings.any? do |sibling|
|
155
|
+
sibling.method?(node.method_name) && !sibling.arguments.empty?
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
152
159
|
def message(range)
|
153
160
|
access_modifier = range.source
|
154
161
|
|
@@ -84,6 +84,7 @@ module RuboCop
|
|
84
84
|
def on_def(node)
|
85
85
|
return unless node.body
|
86
86
|
return unless (rest_args_name, args = use_rest_arguments?(node.arguments))
|
87
|
+
return if args.any?(&:default?)
|
87
88
|
|
88
89
|
node.each_descendant(:send) do |send_node|
|
89
90
|
kwargs_name, block_name = extract_argument_names_from(args)
|
@@ -7,6 +7,10 @@ module RuboCop
|
|
7
7
|
# It can optionally be configured to also require documentation for
|
8
8
|
# non-public methods.
|
9
9
|
#
|
10
|
+
# NOTE: This cop allows `initialize` method because `initialize` is
|
11
|
+
# a special method called from `new`. In some programming languages
|
12
|
+
# they are called constructor to distinguish it from method.
|
13
|
+
#
|
10
14
|
# @example
|
11
15
|
#
|
12
16
|
# # bad
|
@@ -103,6 +107,8 @@ module RuboCop
|
|
103
107
|
PATTERN
|
104
108
|
|
105
109
|
def on_def(node)
|
110
|
+
return if node.method?(:initialize)
|
111
|
+
|
106
112
|
parent = node.parent
|
107
113
|
module_function_node?(parent) ? check(parent) : check(node)
|
108
114
|
end
|
@@ -34,6 +34,7 @@ module RuboCop
|
|
34
34
|
# return cond ? b : c
|
35
35
|
#
|
36
36
|
class MultilineTernaryOperator < Base
|
37
|
+
include CommentsHelp
|
37
38
|
extend AutoCorrector
|
38
39
|
|
39
40
|
MSG_IF = 'Avoid multi-line ternary operators, use `if` or `unless` instead.'
|
@@ -46,9 +47,7 @@ module RuboCop
|
|
46
47
|
message = enforce_single_line_ternary_operator?(node) ? MSG_SINGLE_LINE : MSG_IF
|
47
48
|
|
48
49
|
add_offense(node, message: message) do |corrector|
|
49
|
-
|
50
|
-
|
51
|
-
corrector.replace(node, replacement(node))
|
50
|
+
autocorrect(corrector, node)
|
52
51
|
end
|
53
52
|
end
|
54
53
|
|
@@ -58,6 +57,16 @@ module RuboCop
|
|
58
57
|
node.ternary? && node.multiline?
|
59
58
|
end
|
60
59
|
|
60
|
+
def autocorrect(corrector, node)
|
61
|
+
return unless offense?(node)
|
62
|
+
|
63
|
+
corrector.replace(node, replacement(node))
|
64
|
+
return unless (parent = node.parent)
|
65
|
+
return unless (comments_in_condition = comments_in_condition(node))
|
66
|
+
|
67
|
+
corrector.insert_before(parent, comments_in_condition)
|
68
|
+
end
|
69
|
+
|
61
70
|
def replacement(node)
|
62
71
|
if enforce_single_line_ternary_operator?(node)
|
63
72
|
"#{node.condition.source} ? #{node.if_branch.source} : #{node.else_branch.source}"
|
@@ -72,6 +81,12 @@ module RuboCop
|
|
72
81
|
end
|
73
82
|
end
|
74
83
|
|
84
|
+
def comments_in_condition(node)
|
85
|
+
comments_in_range(node).map do |comment|
|
86
|
+
"#{comment.loc.expression.source}\n"
|
87
|
+
end.join
|
88
|
+
end
|
89
|
+
|
75
90
|
def enforce_single_line_ternary_operator?(node)
|
76
91
|
SINGLE_LINE_TYPES.include?(node.parent&.type) && !use_assignment_method?(node.parent)
|
77
92
|
end
|
@@ -12,10 +12,11 @@ module RuboCop
|
|
12
12
|
#
|
13
13
|
# @example Max: 1 (default)
|
14
14
|
# # bad
|
15
|
-
#
|
15
|
+
# use_multiple_numbered_parameters { _1.call(_2, _3, _4) }
|
16
16
|
#
|
17
17
|
# # good
|
18
|
-
#
|
18
|
+
# array.each { use_array_element_as_numbered_parameter(_1) }
|
19
|
+
# hash.each { use_only_hash_value_as_numbered_parameter(_2) }
|
19
20
|
class NumberedParametersLimit < Base
|
20
21
|
extend TargetRubyVersion
|
21
22
|
extend ExcludeLimit
|
@@ -26,9 +27,10 @@ module RuboCop
|
|
26
27
|
exclude_limit 'Max'
|
27
28
|
|
28
29
|
MSG = 'Avoid using more than %<max>i numbered %<parameter>s; %<count>i detected.'
|
30
|
+
NUMBERED_PARAMETER_PATTERN = /\A_[1-9]\z/.freeze
|
29
31
|
|
30
32
|
def on_numblock(node)
|
31
|
-
|
33
|
+
param_count = numbered_parameter_nodes(node).uniq.count
|
32
34
|
return if param_count <= max_count
|
33
35
|
|
34
36
|
parameter = max_count > 1 ? 'parameters' : 'parameter'
|
@@ -38,6 +40,12 @@ module RuboCop
|
|
38
40
|
|
39
41
|
private
|
40
42
|
|
43
|
+
def numbered_parameter_nodes(node)
|
44
|
+
node.each_descendant(:lvar).select do |lvar_node|
|
45
|
+
lvar_node.source.match?(NUMBERED_PARAMETER_PATTERN)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
41
49
|
def max_count
|
42
50
|
max = cop_config.fetch('Max', DEFAULT_MAX_VALUE)
|
43
51
|
|
@@ -68,7 +68,7 @@ module RuboCop
|
|
68
68
|
replaced_node = ternary_replacement(node)
|
69
69
|
|
70
70
|
return replaced_node unless node.parent
|
71
|
-
return "(#{replaced_node})" if
|
71
|
+
return "(#{replaced_node})" if node.parent.operator_keyword?
|
72
72
|
return "(#{replaced_node})" if node.parent.send_type? && node.parent.operator_method?
|
73
73
|
|
74
74
|
replaced_node
|
@@ -25,7 +25,7 @@ module RuboCop
|
|
25
25
|
|
26
26
|
def on_send(node)
|
27
27
|
return unless (dot = node.loc.dot)
|
28
|
-
return if node.receiver.const_type?
|
28
|
+
return if node.receiver.const_type? || !node.arguments.one?
|
29
29
|
|
30
30
|
_lhs, _op, rhs = *node
|
31
31
|
return if !rhs || method_call_with_parenthesized_arg?(rhs) || anonymous_forwarding?(rhs)
|
@@ -172,7 +172,9 @@ module RuboCop
|
|
172
172
|
end
|
173
173
|
|
174
174
|
def modifier_statement?(node)
|
175
|
-
|
175
|
+
return false unless node
|
176
|
+
|
177
|
+
node.basic_conditional? && node.modifier_form?
|
176
178
|
end
|
177
179
|
|
178
180
|
# An internal class for correcting parallel assignment
|
@@ -36,6 +36,9 @@ module RuboCop
|
|
36
36
|
|
37
37
|
MSG = 'Use double pipes `||` instead.'
|
38
38
|
REDUNDANT_CONDITION = 'This condition is not needed.'
|
39
|
+
ARGUMENT_WITH_OPERATOR_TYPES = %i[
|
40
|
+
splat block_pass forwarded_restarg forwarded_kwrestarg forwarded_args
|
41
|
+
].freeze
|
39
42
|
|
40
43
|
def on_if(node)
|
41
44
|
return if node.elsif_conditional?
|
@@ -150,13 +153,25 @@ module RuboCop
|
|
150
153
|
end
|
151
154
|
|
152
155
|
def single_argument_method?(node)
|
153
|
-
node.send_type?
|
156
|
+
return false if !node.send_type? || node.method?(:[]) || !node.arguments.one?
|
157
|
+
|
158
|
+
!argument_with_operator?(node.first_argument)
|
154
159
|
end
|
155
160
|
|
156
161
|
def same_method?(if_branch, else_branch)
|
157
162
|
if_branch.method?(else_branch.method_name) && if_branch.receiver == else_branch.receiver
|
158
163
|
end
|
159
164
|
|
165
|
+
# If the argument is using an operator, it is an invalid syntax.
|
166
|
+
# e.g. `foo || *bar`, `foo || **bar`, and `foo || &bar`.
|
167
|
+
def argument_with_operator?(argument)
|
168
|
+
return true if ARGUMENT_WITH_OPERATOR_TYPES.include?(argument.type)
|
169
|
+
return false unless argument.hash_type?
|
170
|
+
|
171
|
+
node = argument.children.first
|
172
|
+
node.kwsplat_type? || node.forwarded_kwrestarg_type?
|
173
|
+
end
|
174
|
+
|
160
175
|
def if_source(if_branch, arithmetic_operation)
|
161
176
|
if branches_have_method?(if_branch.parent) && if_branch.parenthesized?
|
162
177
|
if_branch.source.delete_suffix(')')
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Checks for redundant heredoc delimiter quotes.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
#
|
10
|
+
# # bad
|
11
|
+
# do_something(<<~'EOS')
|
12
|
+
# no string interpolation style text
|
13
|
+
# EOS
|
14
|
+
#
|
15
|
+
# # good
|
16
|
+
# do_something(<<~EOS)
|
17
|
+
# no string interpolation style text
|
18
|
+
# EOS
|
19
|
+
#
|
20
|
+
# do_something(<<~'EOS')
|
21
|
+
# #{string_interpolation_style_text_not_evaluated}
|
22
|
+
# EOS
|
23
|
+
#
|
24
|
+
# do_something(<<~'EOS')
|
25
|
+
# Preserve \
|
26
|
+
# newlines
|
27
|
+
# EOS
|
28
|
+
#
|
29
|
+
class RedundantHeredocDelimiterQuotes < Base
|
30
|
+
include Heredoc
|
31
|
+
extend AutoCorrector
|
32
|
+
|
33
|
+
MSG = 'Remove the redundant heredoc delimiter quotes, use `%<replacement>s` instead.'
|
34
|
+
STRING_INTERPOLATION_OR_ESCAPED_CHARACTER_PATTERN = /#(\{|@|\$)|\\/.freeze
|
35
|
+
|
36
|
+
def on_heredoc(node)
|
37
|
+
return if need_heredoc_delimiter_quotes?(node)
|
38
|
+
|
39
|
+
replacement = "#{heredoc_type(node)}#{delimiter_string(node)}"
|
40
|
+
|
41
|
+
add_offense(node, message: format(MSG, replacement: replacement)) do |corrector|
|
42
|
+
corrector.replace(node, replacement)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def need_heredoc_delimiter_quotes?(node)
|
49
|
+
heredoc_delimiter = node.source.delete(heredoc_type(node))
|
50
|
+
return true unless heredoc_delimiter.start_with?("'", '"')
|
51
|
+
|
52
|
+
node.loc.heredoc_end.source.strip.match?(/\W/) ||
|
53
|
+
node.loc.heredoc_body.source.match?(STRING_INTERPOLATION_OR_ESCAPED_CHARACTER_PATTERN)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -42,7 +42,7 @@ module RuboCop
|
|
42
42
|
|
43
43
|
if rhs.send_type?
|
44
44
|
check_send_node(node, rhs, var_name, var_type)
|
45
|
-
elsif
|
45
|
+
elsif rhs.operator_keyword?
|
46
46
|
check_boolean_node(node, rhs, var_name, var_type)
|
47
47
|
end
|
48
48
|
end
|
@@ -76,7 +76,7 @@ module RuboCop
|
|
76
76
|
|
77
77
|
if rhs.send_type?
|
78
78
|
autocorrect_send_node(corrector, node, rhs)
|
79
|
-
elsif
|
79
|
+
elsif rhs.operator_keyword?
|
80
80
|
autocorrect_boolean_node(corrector, node, rhs)
|
81
81
|
end
|
82
82
|
end
|
@@ -33,12 +33,14 @@ module RuboCop
|
|
33
33
|
# "bar" != foo
|
34
34
|
# 42 >= foo
|
35
35
|
# 10 < bar
|
36
|
+
# 99 == CONST
|
36
37
|
#
|
37
38
|
# # good
|
38
39
|
# foo == 99
|
39
40
|
# foo == "bar"
|
40
41
|
# foo <= 42
|
41
42
|
# bar > 10
|
43
|
+
# CONST == 99
|
42
44
|
# "#{interpolation}" == foo
|
43
45
|
# /#{interpolation}/ == foo
|
44
46
|
#
|
@@ -92,9 +94,10 @@ module RuboCop
|
|
92
94
|
def on_send(node)
|
93
95
|
return unless yoda_compatible_condition?(node)
|
94
96
|
return if (equality_only? && non_equality_operator?(node)) ||
|
95
|
-
file_constant_equal_program_name?(node)
|
97
|
+
file_constant_equal_program_name?(node) ||
|
98
|
+
valid_yoda?(node)
|
96
99
|
|
97
|
-
|
100
|
+
add_offense(node) do |corrector|
|
98
101
|
corrector.replace(actual_code_range(node), corrected_code(node))
|
99
102
|
end
|
100
103
|
end
|
@@ -119,11 +122,11 @@ module RuboCop
|
|
119
122
|
lhs = node.receiver
|
120
123
|
rhs = node.first_argument
|
121
124
|
|
122
|
-
return true if (lhs
|
123
|
-
(!lhs
|
125
|
+
return true if (constant_portion?(lhs) && constant_portion?(rhs)) ||
|
126
|
+
(!constant_portion?(lhs) && !constant_portion?(rhs)) ||
|
124
127
|
interpolation?(lhs)
|
125
128
|
|
126
|
-
enforce_yoda? ? lhs
|
129
|
+
enforce_yoda? ? constant_portion?(lhs) : constant_portion?(rhs)
|
127
130
|
end
|
128
131
|
|
129
132
|
def message(node)
|
@@ -137,6 +140,10 @@ module RuboCop
|
|
137
140
|
"#{rhs.source} #{reverse_comparison(node.method_name)} #{lhs.source}"
|
138
141
|
end
|
139
142
|
|
143
|
+
def constant_portion?(node)
|
144
|
+
node.literal? || node.const_type?
|
145
|
+
end
|
146
|
+
|
140
147
|
def actual_code_range(node)
|
141
148
|
range_between(node.loc.expression.begin_pos, node.loc.expression.end_pos)
|
142
149
|
end
|