rubocop 1.17.0 → 1.18.4
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 +70 -29
- data/lib/rubocop.rb +2 -0
- data/lib/rubocop/cli/command/suggest_extensions.rb +3 -3
- data/lib/rubocop/config_loader.rb +1 -1
- data/lib/rubocop/config_loader_resolver.rb +1 -1
- data/lib/rubocop/config_validator.rb +23 -10
- data/lib/rubocop/cop/base.rb +2 -2
- data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -1
- data/lib/rubocop/cop/bundler/gem_version.rb +38 -4
- data/lib/rubocop/cop/corrector.rb +4 -4
- data/lib/rubocop/cop/generator.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -1
- data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/array_alignment.rb +2 -2
- data/lib/rubocop/cop/layout/block_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/class_structure.rb +5 -1
- data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +7 -1
- data/lib/rubocop/cop/layout/comment_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/end_alignment.rb +8 -1
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/first_array_element_indentation.rb +2 -2
- data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +2 -2
- data/lib/rubocop/cop/layout/first_parameter_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/hash_alignment.rb +25 -24
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +1 -1
- data/lib/rubocop/cop/layout/indentation_style.rb +2 -2
- data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +122 -0
- data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +6 -6
- data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +2 -2
- data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +6 -6
- data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +6 -6
- data/lib/rubocop/cop/layout/multiline_method_definition_brace_layout.rb +6 -6
- data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +3 -3
- data/lib/rubocop/cop/layout/parameter_alignment.rb +2 -2
- data/lib/rubocop/cop/layout/space_around_operators.rb +5 -1
- data/lib/rubocop/cop/lint/duplicate_branch.rb +2 -1
- data/lib/rubocop/cop/lint/nested_percent_literal.rb +1 -1
- data/lib/rubocop/cop/lint/percent_string_array.rb +1 -1
- data/lib/rubocop/cop/lint/percent_symbol_array.rb +1 -1
- data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
- data/lib/rubocop/cop/lint/unused_block_argument.rb +1 -1
- data/lib/rubocop/cop/lint/useless_assignment.rb +1 -1
- data/lib/rubocop/cop/lint/useless_times.rb +1 -1
- data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +12 -3
- data/lib/rubocop/cop/mixin/hash_transform_method.rb +6 -1
- data/lib/rubocop/cop/naming/inclusive_language.rb +249 -0
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +2 -2
- data/lib/rubocop/cop/style/block_delimiters.rb +15 -0
- data/lib/rubocop/cop/style/class_and_module_children.rb +14 -0
- data/lib/rubocop/cop/style/comment_annotation.rb +50 -6
- data/lib/rubocop/cop/style/double_cop_disable_directive.rb +1 -7
- data/lib/rubocop/cop/style/eval_with_location.rb +1 -1
- data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +8 -2
- data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
- data/lib/rubocop/cop/style/multiple_comparison.rb +1 -1
- data/lib/rubocop/cop/style/mutable_constant.rb +6 -8
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +1 -1
- data/lib/rubocop/cop/style/quoted_symbols.rb +2 -2
- data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +1 -1
- data/lib/rubocop/cop/style/regexp_literal.rb +3 -2
- data/lib/rubocop/cop/style/single_line_methods.rb +25 -15
- data/lib/rubocop/cop/style/special_global_vars.rb +3 -3
- data/lib/rubocop/cop/style/string_concatenation.rb +32 -5
- data/lib/rubocop/cop/style/string_literals.rb +2 -2
- data/lib/rubocop/cop/style/swap_values.rb +1 -1
- data/lib/rubocop/cop/style/unpack_first.rb +1 -1
- data/lib/rubocop/cop/variable_force/variable_table.rb +1 -1
- data/lib/rubocop/formatter/git_hub_actions_formatter.rb +1 -1
- data/lib/rubocop/options.rb +4 -4
- data/lib/rubocop/rspec/cop_helper.rb +1 -1
- data/lib/rubocop/rspec/expect_offense.rb +1 -1
- data/lib/rubocop/version.rb +1 -1
- metadata +11 -9
@@ -147,9 +147,9 @@ module RuboCop
|
|
147
147
|
include ConfigurableEnforcedStyle
|
148
148
|
|
149
149
|
MSG = 'Memoized variable `%<var>s` does not match ' \
|
150
|
-
|
150
|
+
'method name `%<method>s`. Use `@%<suggested_var>s` instead.'
|
151
151
|
UNDERSCORE_REQUIRED = 'Memoized variable `%<var>s` does not start ' \
|
152
|
-
|
152
|
+
'with `_`. Use `@%<suggested_var>s` instead.'
|
153
153
|
DYNAMIC_DEFINE_METHODS = %i[define_method define_singleton_method].to_set.freeze
|
154
154
|
|
155
155
|
# @!method method_definition?(node)
|
@@ -135,6 +135,7 @@ module RuboCop
|
|
135
135
|
class BlockDelimiters < Base
|
136
136
|
include ConfigurableEnforcedStyle
|
137
137
|
include IgnoredMethods
|
138
|
+
include RangeHelp
|
138
139
|
extend AutoCorrector
|
139
140
|
|
140
141
|
ALWAYS_BRACES_MESSAGE = 'Prefer `{...}` over `do...end` for blocks.'
|
@@ -231,6 +232,11 @@ module RuboCop
|
|
231
232
|
corrector.insert_before(e, ' ') unless whitespace_before?(e)
|
232
233
|
corrector.insert_after(b, ' ') unless whitespace_after?(b)
|
233
234
|
corrector.replace(b, 'do')
|
235
|
+
|
236
|
+
if (comment = processed_source.comment_at_line(e.line))
|
237
|
+
move_comment_before_block(corrector, comment, loc.node, e)
|
238
|
+
end
|
239
|
+
|
234
240
|
corrector.replace(e, 'end')
|
235
241
|
end
|
236
242
|
|
@@ -252,6 +258,15 @@ module RuboCop
|
|
252
258
|
/\s/.match?(range.source_buffer.source[range.begin_pos + length, 1])
|
253
259
|
end
|
254
260
|
|
261
|
+
def move_comment_before_block(corrector, comment, block_node, closing_brace)
|
262
|
+
range = range_between(closing_brace.end_pos, comment.loc.expression.end_pos)
|
263
|
+
|
264
|
+
corrector.remove(range_with_surrounding_space(range: range, side: :right))
|
265
|
+
corrector.insert_after(closing_brace, "\n")
|
266
|
+
|
267
|
+
corrector.insert_before(block_node, "#{comment.text}\n")
|
268
|
+
end
|
269
|
+
|
255
270
|
def get_blocks(node, &block)
|
256
271
|
case node.type
|
257
272
|
when :block
|
@@ -84,6 +84,7 @@ module RuboCop
|
|
84
84
|
def compact_definition(corrector, node)
|
85
85
|
compact_node(corrector, node)
|
86
86
|
remove_end(corrector, node.body)
|
87
|
+
unindent(corrector, node)
|
87
88
|
end
|
88
89
|
|
89
90
|
def compact_node(corrector, node)
|
@@ -114,6 +115,19 @@ module RuboCop
|
|
114
115
|
corrector.remove(range)
|
115
116
|
end
|
116
117
|
|
118
|
+
def configured_indentation_width
|
119
|
+
config.for_badge(Layout::IndentationWidth.badge).fetch('Width', 2)
|
120
|
+
end
|
121
|
+
|
122
|
+
def unindent(corrector, node)
|
123
|
+
return if node.body.children.last.nil?
|
124
|
+
|
125
|
+
column_delta = configured_indentation_width - leading_spaces(node.body.children.last).size
|
126
|
+
return if column_delta.zero?
|
127
|
+
|
128
|
+
AlignmentCorrector.correct(corrector, processed_source, node, column_delta)
|
129
|
+
end
|
130
|
+
|
117
131
|
def leading_spaces(node)
|
118
132
|
node.source_range.source_line[/\A\s*/]
|
119
133
|
end
|
@@ -12,7 +12,7 @@ module RuboCop
|
|
12
12
|
# incorrect registering of keywords (eg. `review`) inside a paragraph as an
|
13
13
|
# annotation.
|
14
14
|
#
|
15
|
-
# @example
|
15
|
+
# @example RequireColon: true (default)
|
16
16
|
# # bad
|
17
17
|
# # TODO make better
|
18
18
|
#
|
@@ -36,14 +36,36 @@ module RuboCop
|
|
36
36
|
#
|
37
37
|
# # good
|
38
38
|
# # OPTIMIZE: does not work
|
39
|
+
#
|
40
|
+
# @example RequireColon: false
|
41
|
+
# # bad
|
42
|
+
# # TODO: make better
|
43
|
+
#
|
44
|
+
# # good
|
45
|
+
# # TODO make better
|
46
|
+
#
|
47
|
+
# # bad
|
48
|
+
# # fixme does not work
|
49
|
+
#
|
50
|
+
# # good
|
51
|
+
# # FIXME does not work
|
52
|
+
#
|
53
|
+
# # bad
|
54
|
+
# # Optimize does not work
|
55
|
+
#
|
56
|
+
# # good
|
57
|
+
# # OPTIMIZE does not work
|
39
58
|
class CommentAnnotation < Base
|
40
59
|
include AnnotationComment
|
41
60
|
include RangeHelp
|
42
61
|
extend AutoCorrector
|
43
62
|
|
44
|
-
|
45
|
-
|
46
|
-
|
63
|
+
MSG_COLON_STYLE = 'Annotation keywords like `%<keyword>s` should be all ' \
|
64
|
+
'upper case, followed by a colon, and a space, ' \
|
65
|
+
'then a note describing the problem.'
|
66
|
+
MSG_SPACE_STYLE = 'Annotation keywords like `%<keyword>s` should be all ' \
|
67
|
+
'upper case, followed by a space, ' \
|
68
|
+
'then a note describing the problem.'
|
47
69
|
MISSING_NOTE = 'Annotation comment, with keyword `%<keyword>s`, is missing a note.'
|
48
70
|
|
49
71
|
def on_new_investigation
|
@@ -63,13 +85,15 @@ module RuboCop
|
|
63
85
|
private
|
64
86
|
|
65
87
|
def register_offense(range, note, first_word)
|
88
|
+
message = requires_colon? ? MSG_COLON_STYLE : MSG_SPACE_STYLE
|
89
|
+
|
66
90
|
add_offense(
|
67
91
|
range,
|
68
|
-
message: format(note ?
|
92
|
+
message: format(note ? message : MISSING_NOTE, keyword: first_word)
|
69
93
|
) do |corrector|
|
70
94
|
next if note.nil?
|
71
95
|
|
72
|
-
corrector
|
96
|
+
correct_offense(corrector, range, first_word)
|
73
97
|
end
|
74
98
|
end
|
75
99
|
|
@@ -92,8 +116,28 @@ module RuboCop
|
|
92
116
|
end
|
93
117
|
|
94
118
|
def correct_annotation?(first_word, colon, space, note)
|
119
|
+
return correct_colon_annotation?(first_word, colon, space, note) if requires_colon?
|
120
|
+
|
121
|
+
correct_space_annotation?(first_word, colon, space, note)
|
122
|
+
end
|
123
|
+
|
124
|
+
def correct_colon_annotation?(first_word, colon, space, note)
|
95
125
|
keyword?(first_word) && (colon && space && note || !colon && !note)
|
96
126
|
end
|
127
|
+
|
128
|
+
def correct_space_annotation?(first_word, colon, space, note)
|
129
|
+
keyword?(first_word) && (!colon && space && note || !colon && !note)
|
130
|
+
end
|
131
|
+
|
132
|
+
def correct_offense(corrector, range, first_word)
|
133
|
+
return corrector.replace(range, "#{first_word.upcase}: ") if requires_colon?
|
134
|
+
|
135
|
+
corrector.replace(range, "#{first_word.upcase} ")
|
136
|
+
end
|
137
|
+
|
138
|
+
def requires_colon?
|
139
|
+
cop_config['RequireColon']
|
140
|
+
end
|
97
141
|
end
|
98
142
|
end
|
99
143
|
end
|
@@ -36,13 +36,7 @@ module RuboCop
|
|
36
36
|
next unless comment.text.scan(/# rubocop:(?:disable|todo)/).size > 1
|
37
37
|
|
38
38
|
add_offense(comment) do |corrector|
|
39
|
-
|
40
|
-
'# rubocop:disable'
|
41
|
-
else
|
42
|
-
'# rubocop:todo'
|
43
|
-
end
|
44
|
-
|
45
|
-
corrector.replace(comment, comment.text[/#{prefix} \S+/])
|
39
|
+
corrector.replace(comment, comment.text.gsub(%r{ # rubocop:(disable|todo)}, ','))
|
46
40
|
end
|
47
41
|
end
|
48
42
|
end
|
@@ -43,7 +43,7 @@ module RuboCop
|
|
43
43
|
# RUBY
|
44
44
|
#
|
45
45
|
# This cop works only when a string literal is given as a code string.
|
46
|
-
# No
|
46
|
+
# No offense is reported if a string variable is given as below:
|
47
47
|
#
|
48
48
|
# @example
|
49
49
|
# # not checked
|
@@ -5,11 +5,17 @@ module RuboCop
|
|
5
5
|
module Style
|
6
6
|
# This cop is designed to help you transition from mutable string literals
|
7
7
|
# to frozen string literals.
|
8
|
-
# It will add the
|
9
|
-
# files to enable frozen string literals. Frozen string literals may be
|
8
|
+
# It will add the `# frozen_string_literal: true` magic comment to the top
|
9
|
+
# of files to enable frozen string literals. Frozen string literals may be
|
10
10
|
# default in future Ruby. The comment will be added below a shebang and
|
11
11
|
# encoding comment.
|
12
12
|
#
|
13
|
+
# Note that the cop will ignore files where the comment exists but is set
|
14
|
+
# to `false` instead of `true`.
|
15
|
+
#
|
16
|
+
# To require a blank line after this comment, please see
|
17
|
+
# `Layout/EmptyLineAfterMagicComment` cop.
|
18
|
+
#
|
13
19
|
# @example EnforcedStyle: always (default)
|
14
20
|
# # The `always` style will always add the frozen string literal comment
|
15
21
|
# # to a file, regardless of the Ruby version or if `freeze` or `<<` are
|
@@ -44,7 +44,7 @@ module RuboCop
|
|
44
44
|
extend AutoCorrector
|
45
45
|
|
46
46
|
MSG = 'Avoid comparing a variable with multiple items ' \
|
47
|
-
|
47
|
+
'in a conditional, use `Array#include?` instead.'
|
48
48
|
|
49
49
|
def on_new_investigation
|
50
50
|
@last_comparison = nil
|
@@ -61,13 +61,12 @@ module RuboCop
|
|
61
61
|
|
62
62
|
def on_casgn(node)
|
63
63
|
_scope, _const_name, value = *node
|
64
|
-
|
65
|
-
|
64
|
+
if value.nil? # This is only the case for `CONST += ...` or similarg66
|
65
|
+
parent = node.parent
|
66
|
+
return unless parent.or_asgn_type? # We only care about `CONST ||= ...`
|
66
67
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
return unless lhs&.casgn_type?
|
68
|
+
value = parent.children.last
|
69
|
+
end
|
71
70
|
|
72
71
|
on_assignment(value)
|
73
72
|
end
|
@@ -118,14 +117,13 @@ module RuboCop
|
|
118
117
|
end
|
119
118
|
|
120
119
|
def mutable_literal?(value)
|
121
|
-
return false if value.nil?
|
122
120
|
return false if frozen_regexp_or_range_literals?(value)
|
123
121
|
|
124
122
|
value.mutable_literal?
|
125
123
|
end
|
126
124
|
|
127
125
|
def immutable_literal?(node)
|
128
|
-
|
126
|
+
frozen_regexp_or_range_literals?(node) || node.immutable_literal?
|
129
127
|
end
|
130
128
|
|
131
129
|
def frozen_string_literal?(node)
|
@@ -36,9 +36,9 @@ module RuboCop
|
|
36
36
|
extend AutoCorrector
|
37
37
|
|
38
38
|
MSG_SINGLE = "Prefer single-quoted symbols when you don't need string interpolation " \
|
39
|
-
|
39
|
+
'or special symbols.'
|
40
40
|
MSG_DOUBLE = 'Prefer double-quoted symbols unless you need single quotes to ' \
|
41
|
-
|
41
|
+
'avoid extra backslashes for escaping.'
|
42
42
|
|
43
43
|
def on_sym(node)
|
44
44
|
return unless quoted?(node)
|
@@ -32,7 +32,7 @@ module RuboCop
|
|
32
32
|
|
33
33
|
REQUIRES_ESCAPE_OUTSIDE_CHAR_CLASS_CHARS = '.*+?{}()|$'.chars.freeze
|
34
34
|
MSG_REDUNDANT_CHARACTER_CLASS = 'Redundant single-element character class, ' \
|
35
|
-
|
35
|
+
'`%<char_class>s` can be replaced with `%<element>s`.'
|
36
36
|
|
37
37
|
def on_regexp(node)
|
38
38
|
each_redundant_character_class(node) do |loc|
|
@@ -117,7 +117,7 @@ module RuboCop
|
|
117
117
|
def allowed_percent_r_literal?(node)
|
118
118
|
style == :slashes && contains_disallowed_slash?(node) ||
|
119
119
|
style == :percent_r ||
|
120
|
-
allowed_mixed_percent_r?(node) ||
|
120
|
+
allowed_mixed_percent_r?(node) || allowed_omit_parentheses_with_percent_r_literal?(node)
|
121
121
|
end
|
122
122
|
|
123
123
|
def allowed_mixed_percent_r?(node)
|
@@ -149,8 +149,9 @@ module RuboCop
|
|
149
149
|
config.for_cop('Style/PercentLiteralDelimiters') ['PreferredDelimiters']['%r'].chars
|
150
150
|
end
|
151
151
|
|
152
|
-
def
|
152
|
+
def allowed_omit_parentheses_with_percent_r_literal?(node)
|
153
153
|
return false unless node.parent&.call_type?
|
154
|
+
return true if node.content.start_with?(' ')
|
154
155
|
|
155
156
|
enforced_style = config.for_cop('Style/MethodCallWithArgsParentheses')['EnforcedStyle']
|
156
157
|
|
@@ -36,6 +36,7 @@ module RuboCop
|
|
36
36
|
extend AutoCorrector
|
37
37
|
|
38
38
|
MSG = 'Avoid single-line method definitions.'
|
39
|
+
NOT_SUPPORTED_ENDLESS_METHOD_BODY_TYPES = %i[return break next].freeze
|
39
40
|
|
40
41
|
def on_def(node)
|
41
42
|
return unless node.single_line?
|
@@ -62,29 +63,24 @@ module RuboCop
|
|
62
63
|
|
63
64
|
def correct_to_endless?(body_node)
|
64
65
|
return false if target_ruby_version < 3.0
|
65
|
-
|
66
|
-
endless_method_config = config.for_cop('Style/EndlessMethod')
|
67
|
-
|
68
|
-
return false unless endless_method_config['Enabled']
|
69
|
-
return false if endless_method_config['EnforcedStyle'] == 'disallow'
|
66
|
+
return false if disallow_endless_method_style?
|
70
67
|
return false unless body_node
|
71
|
-
return false if body_node.parent.assignment_method?
|
68
|
+
return false if body_node.parent.assignment_method? ||
|
69
|
+
NOT_SUPPORTED_ENDLESS_METHOD_BODY_TYPES.include?(body_node.type)
|
72
70
|
|
73
71
|
!(body_node.begin_type? || body_node.kwbegin_type?)
|
74
72
|
end
|
75
73
|
|
76
74
|
def correct_to_multiline(corrector, node)
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
75
|
+
if (body = node.body) && body.begin_type? && body.parenthesized_call?
|
76
|
+
break_line_before(corrector, node, body)
|
77
|
+
else
|
78
|
+
each_part(body) do |part|
|
79
|
+
break_line_before(corrector, node, part)
|
80
|
+
end
|
82
81
|
end
|
83
82
|
|
84
|
-
|
85
|
-
range: node.loc.end, node: node, corrector: corrector,
|
86
|
-
indent_steps: 0, configured_width: configured_indentation_width
|
87
|
-
)
|
83
|
+
break_line_before(corrector, node, node.loc.end, indent_steps: 0)
|
88
84
|
|
89
85
|
move_comment(node, corrector)
|
90
86
|
end
|
@@ -98,6 +94,13 @@ module RuboCop
|
|
98
94
|
corrector.replace(node, replacement)
|
99
95
|
end
|
100
96
|
|
97
|
+
def break_line_before(corrector, node, range, indent_steps: 1)
|
98
|
+
LineBreakCorrector.break_line_before(
|
99
|
+
range: range, node: node, corrector: corrector,
|
100
|
+
configured_width: configured_indentation_width, indent_steps: indent_steps
|
101
|
+
)
|
102
|
+
end
|
103
|
+
|
101
104
|
def each_part(body)
|
102
105
|
return unless body
|
103
106
|
|
@@ -129,6 +132,13 @@ module RuboCop
|
|
129
132
|
def require_parentheses?(method_body)
|
130
133
|
method_body.send_type? && !method_body.arguments.empty? && !method_body.comparison_method?
|
131
134
|
end
|
135
|
+
|
136
|
+
def disallow_endless_method_style?
|
137
|
+
endless_method_config = config.for_cop('Style/EndlessMethod')
|
138
|
+
return false unless endless_method_config['Enabled']
|
139
|
+
|
140
|
+
endless_method_config['EnforcedStyle'] == 'disallow'
|
141
|
+
end
|
132
142
|
end
|
133
143
|
end
|
134
144
|
end
|
@@ -53,10 +53,10 @@ module RuboCop
|
|
53
53
|
extend AutoCorrector
|
54
54
|
|
55
55
|
MSG_BOTH = 'Prefer `%<prefer>s` from the stdlib \'English\' ' \
|
56
|
-
|
57
|
-
|
56
|
+
'module (don\'t forget to require it) or `%<regular>s` over ' \
|
57
|
+
'`%<global>s`.'
|
58
58
|
MSG_ENGLISH = 'Prefer `%<prefer>s` from the stdlib \'English\' ' \
|
59
|
-
|
59
|
+
'module (don\'t forget to require it) over `%<global>s`.'
|
60
60
|
MSG_REGULAR = 'Prefer `%<prefer>s` over `%<global>s`.'
|
61
61
|
|
62
62
|
ENGLISH_VARS = { # rubocop:disable Style/MutableConstant
|
@@ -15,18 +15,37 @@ module RuboCop
|
|
15
15
|
# lines, this cop does not register an offense; instead,
|
16
16
|
# `Style/LineEndConcatenation` will pick up the offense if enabled.
|
17
17
|
#
|
18
|
-
#
|
18
|
+
# Two modes are supported:
|
19
|
+
# 1. `aggressive` style checks and corrects all occurrences of `+` where
|
20
|
+
# either the left or right side of `+` is a string literal.
|
21
|
+
# 2. `conservative` style on the other hand, checks and corrects only if
|
22
|
+
# left side (receiver of `+` method call) is a string literal.
|
23
|
+
# This is useful when the receiver is some expression that returns string like `Pathname`
|
24
|
+
# instead of a string literal.
|
25
|
+
#
|
26
|
+
# @example Mode: aggressive (default)
|
19
27
|
# # bad
|
20
28
|
# email_with_name = user.name + ' <' + user.email + '>'
|
29
|
+
# Pathname.new('/') + 'test'
|
21
30
|
#
|
22
31
|
# # good
|
23
32
|
# email_with_name = "#{user.name} <#{user.email}>"
|
24
33
|
# email_with_name = format('%s <%s>', user.name, user.email)
|
34
|
+
# "#{Pathname.new('/')}test"
|
25
35
|
#
|
26
36
|
# # accepted, line-end concatenation
|
27
37
|
# name = 'First' +
|
28
38
|
# 'Last'
|
29
39
|
#
|
40
|
+
# @example Mode: conservative
|
41
|
+
# # bad
|
42
|
+
# 'Hello' + user.name
|
43
|
+
#
|
44
|
+
# # good
|
45
|
+
# "Hello #{user.name}"
|
46
|
+
# user.name + '!!'
|
47
|
+
# Pathname.new('/') + 'test'
|
48
|
+
#
|
30
49
|
class StringConcatenation < Base
|
31
50
|
include Util
|
32
51
|
include RangeHelp
|
@@ -52,10 +71,15 @@ module RuboCop
|
|
52
71
|
return if line_end_concatenation?(node)
|
53
72
|
|
54
73
|
topmost_plus_node = find_topmost_plus_node(node)
|
74
|
+
parts = collect_parts(topmost_plus_node)
|
75
|
+
return unless parts[0..-2].any? { |receiver_node| offensive_for_mode?(receiver_node) }
|
55
76
|
|
56
|
-
parts
|
57
|
-
|
77
|
+
register_offense(topmost_plus_node, parts)
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
58
81
|
|
82
|
+
def register_offense(topmost_plus_node, parts)
|
59
83
|
add_offense(topmost_plus_node) do |corrector|
|
60
84
|
correctable_parts = parts.none? { |part| uncorrectable?(part) }
|
61
85
|
if correctable_parts && !corrected_ancestor?(topmost_plus_node)
|
@@ -67,7 +91,10 @@ module RuboCop
|
|
67
91
|
end
|
68
92
|
end
|
69
93
|
|
70
|
-
|
94
|
+
def offensive_for_mode?(receiver_node)
|
95
|
+
mode = cop_config['Mode'].to_sym
|
96
|
+
mode == :aggressive || mode == :conservative && receiver_node.str_type?
|
97
|
+
end
|
71
98
|
|
72
99
|
def line_end_concatenation?(node)
|
73
100
|
# If the concatenation happens at the end of the line,
|
@@ -87,7 +114,7 @@ module RuboCop
|
|
87
114
|
current
|
88
115
|
end
|
89
116
|
|
90
|
-
def collect_parts(node, parts)
|
117
|
+
def collect_parts(node, parts = [])
|
91
118
|
return unless node
|
92
119
|
|
93
120
|
if plus_node?(node)
|