rubocop 1.6.0 → 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/LICENSE.txt +1 -1
- data/README.md +3 -2
- data/config/default.yml +142 -19
- data/lib/rubocop.rb +15 -1
- data/lib/rubocop/cli/command/auto_genenerate_config.rb +5 -4
- data/lib/rubocop/comment_config.rb +6 -6
- data/lib/rubocop/config.rb +10 -7
- data/lib/rubocop/config_loader.rb +11 -14
- data/lib/rubocop/config_loader_resolver.rb +21 -4
- data/lib/rubocop/config_obsoletion.rb +5 -3
- data/lib/rubocop/config_obsoletion/extracted_cop.rb +6 -6
- data/lib/rubocop/config_store.rb +12 -1
- data/lib/rubocop/cop/base.rb +1 -1
- data/lib/rubocop/cop/gemspec/required_ruby_version.rb +3 -2
- data/lib/rubocop/cop/generator.rb +1 -3
- data/lib/rubocop/cop/internal_affairs.rb +6 -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/internal_affairs/style_detected_api_use.rb +145 -0
- data/lib/rubocop/cop/layout/class_structure.rb +7 -2
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +19 -3
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +14 -0
- data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -2
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +3 -10
- data/lib/rubocop/cop/layout/space_around_equals_in_parameter_default.rb +1 -0
- data/lib/rubocop/cop/layout/space_before_block_braces.rb +2 -0
- data/lib/rubocop/cop/layout/space_before_brackets.rb +62 -0
- data/lib/rubocop/cop/layout/space_inside_block_braces.rb +13 -10
- data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +2 -2
- data/lib/rubocop/cop/lint/ambiguous_assignment.rb +59 -0
- data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +7 -2
- data/lib/rubocop/cop/lint/deprecated_constants.rb +75 -0
- data/lib/rubocop/cop/lint/duplicate_branch.rb +64 -2
- data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +44 -0
- data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +10 -6
- 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/redundant_cop_disable_directive.rb +2 -1
- data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +50 -0
- data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +50 -17
- data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -11
- data/lib/rubocop/cop/lint/symbol_conversion.rb +102 -0
- data/lib/rubocop/cop/lint/triple_quotes.rb +71 -0
- data/lib/rubocop/cop/lint/unreachable_loop.rb +17 -0
- data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
- data/lib/rubocop/cop/mixin/allowed_identifiers.rb +18 -0
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +5 -0
- data/lib/rubocop/cop/mixin/comments_help.rb +1 -11
- data/lib/rubocop/cop/mixin/first_element_line_break.rb +1 -1
- data/lib/rubocop/cop/mixin/uncommunicative_name.rb +5 -1
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +59 -5
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +38 -5
- data/lib/rubocop/cop/naming/variable_name.rb +2 -0
- data/lib/rubocop/cop/naming/variable_number.rb +2 -9
- data/lib/rubocop/cop/registry.rb +10 -0
- data/lib/rubocop/cop/severity.rb +3 -3
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +3 -1
- data/lib/rubocop/cop/style/ascii_comments.rb +1 -1
- data/lib/rubocop/cop/style/collection_methods.rb +14 -1
- data/lib/rubocop/cop/style/commented_keyword.rb +22 -5
- data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +49 -9
- data/lib/rubocop/cop/style/empty_literal.rb +6 -2
- data/lib/rubocop/cop/style/endless_method.rb +102 -0
- data/lib/rubocop/cop/style/eval_with_location.rb +63 -34
- data/lib/rubocop/cop/style/explicit_block_argument.rb +10 -0
- data/lib/rubocop/cop/style/float_division.rb +3 -0
- data/lib/rubocop/cop/style/for.rb +2 -0
- data/lib/rubocop/cop/style/format_string_token.rb +18 -2
- data/lib/rubocop/cop/style/hash_except.rb +95 -0
- data/lib/rubocop/cop/style/hash_like_case.rb +2 -1
- data/lib/rubocop/cop/style/if_inside_else.rb +22 -10
- data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +96 -0
- data/lib/rubocop/cop/style/keyword_parameters_order.rb +12 -2
- data/lib/rubocop/cop/style/lambda_call.rb +2 -1
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +7 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +16 -6
- data/lib/rubocop/cop/style/method_def_parentheses.rb +7 -0
- data/lib/rubocop/cop/style/multiline_method_signature.rb +26 -1
- data/lib/rubocop/cop/style/multiline_when_then.rb +3 -1
- data/lib/rubocop/cop/style/mutable_constant.rb +13 -3
- data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +4 -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/raise_args.rb +5 -2
- data/lib/rubocop/cop/style/redundant_argument.rb +7 -1
- data/lib/rubocop/cop/style/redundant_freeze.rb +8 -4
- data/lib/rubocop/cop/style/redundant_return.rb +1 -1
- data/lib/rubocop/cop/style/single_line_methods.rb +34 -2
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +29 -5
- data/lib/rubocop/cop/style/string_concatenation.rb +1 -1
- data/lib/rubocop/cop/style/symbol_proc.rb +5 -4
- data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/while_until_modifier.rb +2 -4
- data/lib/rubocop/cop/util.rb +3 -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 +10 -10
- data/lib/rubocop/rspec/cop_helper.rb +0 -4
- data/lib/rubocop/rspec/expect_offense.rb +37 -22
- data/lib/rubocop/runner.rb +17 -1
- data/lib/rubocop/target_finder.rb +4 -2
- data/lib/rubocop/target_ruby.rb +47 -11
- data/lib/rubocop/util.rb +16 -0
- data/lib/rubocop/version.rb +8 -2
- metadata +26 -7
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# This cop checks for uses of numbered parameter assignment.
|
7
|
+
# It emulates the following warning in Ruby 2.7:
|
8
|
+
#
|
9
|
+
# % ruby -ve '_1 = :value'
|
10
|
+
# ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-darwin19]
|
11
|
+
# -e:1: warning: `_1' is reserved for numbered parameter; consider another name
|
12
|
+
#
|
13
|
+
# Assiging to numbered parameter (from `_1` to `_9`) cause an error in Ruby 3.0.
|
14
|
+
#
|
15
|
+
# % ruby -ve '_1 = :value'
|
16
|
+
# ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-darwin19]
|
17
|
+
# -e:1: _1 is reserved for numbered parameter
|
18
|
+
#
|
19
|
+
# NOTE: The parametered parameters are from `_1` to `_9`. This cop checks `_0`, and over `_10`
|
20
|
+
# as well to prevent confusion.
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
#
|
24
|
+
# # bad
|
25
|
+
# _1 = :value
|
26
|
+
#
|
27
|
+
# # good
|
28
|
+
# non_numbered_parameter_name = :value
|
29
|
+
#
|
30
|
+
class NumberedParameterAssignment < Base
|
31
|
+
NUM_PARAM_MSG = '`_%<number>s` is reserved for numbered parameter; consider another name.'
|
32
|
+
LVAR_MSG = '`_%<number>s` is similar to numbered parameter; consider another name.'
|
33
|
+
NUMBERED_PARAMETER_RANGE = (1..9).freeze
|
34
|
+
|
35
|
+
def on_lvasgn(node)
|
36
|
+
lhs, _rhs = *node
|
37
|
+
return unless /\A_(\d+)\z/ =~ lhs
|
38
|
+
|
39
|
+
number = Regexp.last_match(1).to_i
|
40
|
+
template = NUMBERED_PARAMETER_RANGE.include?(number) ? NUM_PARAM_MSG : LVAR_MSG
|
41
|
+
|
42
|
+
add_offense(node, message: format(template, number: number))
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# This cop checks for unintended or-assignment to a constant.
|
7
|
+
#
|
8
|
+
# Constants should always be assigned in the same location. And its value
|
9
|
+
# should always be the same. If constants are assigned in multiple
|
10
|
+
# locations, the result may vary depending on the order of `require`.
|
11
|
+
#
|
12
|
+
# Also, if you already have such an implementation, auto-correction may
|
13
|
+
# change the result.
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
#
|
17
|
+
# # bad
|
18
|
+
# CONST ||= 1
|
19
|
+
#
|
20
|
+
# # good
|
21
|
+
# CONST = 1
|
22
|
+
#
|
23
|
+
class OrAssignmentToConstant < Base
|
24
|
+
extend AutoCorrector
|
25
|
+
|
26
|
+
MSG = 'Avoid using or-assignment with constants.'
|
27
|
+
|
28
|
+
def on_or_asgn(node)
|
29
|
+
lhs, _rhs = *node
|
30
|
+
return unless lhs&.casgn_type?
|
31
|
+
|
32
|
+
add_offense(node.loc.operator) do |corrector|
|
33
|
+
corrector.replace(node.loc.operator, '=')
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -61,7 +61,8 @@ module RuboCop
|
|
61
61
|
end
|
62
62
|
|
63
63
|
def comment_range_with_surrounding_space(range)
|
64
|
-
if previous_line_blank?(range)
|
64
|
+
if previous_line_blank?(range) &&
|
65
|
+
processed_source.comment_config.comment_only_line?(range.line)
|
65
66
|
# When the previous line is blank, it should be retained
|
66
67
|
range_with_surrounding_space(range: range, side: :right)
|
67
68
|
else
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# Sort globbed results by default in Ruby 3.0.
|
7
|
+
# This cop checks for redundant `sort` method to `Dir.glob` and `Dir[]`.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
#
|
11
|
+
# # bad
|
12
|
+
# Dir.glob('./lib/**/*.rb').sort.each do |file|
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# Dir['./lib/**/*.rb'].sort.each do |file|
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# # good
|
19
|
+
# Dir.glob('./lib/**/*.rb').each do |file|
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# Dir['./lib/**/*.rb'].each do |file|
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
class RedundantDirGlobSort < Base
|
26
|
+
extend AutoCorrector
|
27
|
+
extend TargetRubyVersion
|
28
|
+
|
29
|
+
minimum_target_ruby_version 3.0
|
30
|
+
|
31
|
+
MSG = 'Remove redundant `sort`.'
|
32
|
+
RESTRICT_ON_SEND = %i[sort].freeze
|
33
|
+
GLOB_METHODS = %i[glob []].freeze
|
34
|
+
|
35
|
+
def on_send(node)
|
36
|
+
return unless (receiver = node.receiver)
|
37
|
+
return unless receiver.receiver&.const_type? && receiver.receiver.short_name == :Dir
|
38
|
+
return unless GLOB_METHODS.include?(receiver.method_name)
|
39
|
+
|
40
|
+
selector = node.loc.selector
|
41
|
+
|
42
|
+
add_offense(selector) do |corrector|
|
43
|
+
corrector.remove(selector)
|
44
|
+
corrector.remove(node.loc.dot)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -8,47 +8,66 @@ module RuboCop
|
|
8
8
|
# @example
|
9
9
|
#
|
10
10
|
# # bad
|
11
|
-
#
|
12
11
|
# a = *[1, 2, 3]
|
13
12
|
# a = *'a'
|
14
13
|
# a = *1
|
15
|
-
#
|
16
|
-
# begin
|
17
|
-
# foo
|
18
|
-
# rescue *[StandardError, ApplicationError]
|
19
|
-
# bar
|
20
|
-
# end
|
21
|
-
#
|
22
|
-
# case foo
|
23
|
-
# when *[1, 2, 3]
|
24
|
-
# bar
|
25
|
-
# else
|
26
|
-
# baz
|
27
|
-
# end
|
28
|
-
#
|
29
|
-
# @example
|
14
|
+
# ['a', 'b', *%w(c d e), 'f', 'g']
|
30
15
|
#
|
31
16
|
# # good
|
32
|
-
#
|
33
17
|
# c = [1, 2, 3]
|
34
18
|
# a = *c
|
35
19
|
# a, b = *c
|
36
20
|
# a, *b = *c
|
37
21
|
# a = *1..10
|
38
22
|
# a = ['a']
|
23
|
+
# ['a', 'b', 'c', 'd', 'e', 'f', 'g']
|
39
24
|
#
|
25
|
+
# # bad
|
26
|
+
# do_something(*['foo', 'bar', 'baz'])
|
27
|
+
#
|
28
|
+
# # good
|
29
|
+
# do_something('foo', 'bar', 'baz')
|
30
|
+
#
|
31
|
+
# # bad
|
32
|
+
# begin
|
33
|
+
# foo
|
34
|
+
# rescue *[StandardError, ApplicationError]
|
35
|
+
# bar
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# # good
|
40
39
|
# begin
|
41
40
|
# foo
|
42
41
|
# rescue StandardError, ApplicationError
|
43
42
|
# bar
|
44
43
|
# end
|
45
44
|
#
|
45
|
+
# # bad
|
46
|
+
# case foo
|
47
|
+
# when *[1, 2, 3]
|
48
|
+
# bar
|
49
|
+
# else
|
50
|
+
# baz
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# # good
|
46
54
|
# case foo
|
47
55
|
# when 1, 2, 3
|
48
56
|
# bar
|
49
57
|
# else
|
50
58
|
# baz
|
51
59
|
# end
|
60
|
+
#
|
61
|
+
# @example AllowPercentLiteralArrayArgument: true (default)
|
62
|
+
#
|
63
|
+
# # good
|
64
|
+
# do_something(*%w[foo bar baz])
|
65
|
+
#
|
66
|
+
# @example AllowPercentLiteralArrayArgument: false
|
67
|
+
#
|
68
|
+
# # bad
|
69
|
+
# do_something(*%w[foo bar baz])
|
70
|
+
#
|
52
71
|
class RedundantSplatExpansion < Base
|
53
72
|
extend AutoCorrector
|
54
73
|
|
@@ -75,6 +94,9 @@ module RuboCop
|
|
75
94
|
redundant_splat_expansion(node) do
|
76
95
|
if array_splat?(node) &&
|
77
96
|
(method_argument?(node) || part_of_an_array?(node))
|
97
|
+
return if allow_percent_literal_array_argument? &&
|
98
|
+
use_percent_literal_array_argument?(node)
|
99
|
+
|
78
100
|
add_offense(node, message: ARRAY_PARAM_MSG) do |corrector|
|
79
101
|
autocorrect(corrector, node)
|
80
102
|
end
|
@@ -170,6 +192,17 @@ module RuboCop
|
|
170
192
|
elements.join(', ')
|
171
193
|
end
|
172
194
|
end
|
195
|
+
|
196
|
+
def use_percent_literal_array_argument?(node)
|
197
|
+
argument = node.children.first
|
198
|
+
|
199
|
+
node.parent.send_type? &&
|
200
|
+
(argument.percent_literal?(:string) || argument.percent_literal?(:symbol))
|
201
|
+
end
|
202
|
+
|
203
|
+
def allow_percent_literal_array_argument?
|
204
|
+
cop_config.fetch('AllowPercentLiteralArrayArgument', true)
|
205
|
+
end
|
173
206
|
end
|
174
207
|
end
|
175
208
|
end
|
@@ -106,23 +106,13 @@ module RuboCop
|
|
106
106
|
error && error.ancestors[1] == SystemCallError
|
107
107
|
end
|
108
108
|
|
109
|
-
def silence_warnings
|
110
|
-
# Replaces Kernel::silence_warnings since it hides any warnings,
|
111
|
-
# including the RuboCop ones
|
112
|
-
old_verbose = $VERBOSE
|
113
|
-
$VERBOSE = nil
|
114
|
-
yield
|
115
|
-
ensure
|
116
|
-
$VERBOSE = old_verbose
|
117
|
-
end
|
118
|
-
|
119
109
|
def evaluate_exceptions(group)
|
120
110
|
rescued_exceptions = group.exceptions
|
121
111
|
|
122
112
|
if rescued_exceptions.any?
|
123
113
|
rescued_exceptions.each_with_object([]) do |exception, converted|
|
124
114
|
begin
|
125
|
-
silence_warnings do
|
115
|
+
RuboCop::Util.silence_warnings do
|
126
116
|
# Avoid printing deprecation warnings about constants
|
127
117
|
converted << Kernel.const_get(exception.source)
|
128
118
|
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# This cop checks for uses of literal strings converted to
|
7
|
+
# a symbol where a literal symbol could be used instead.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# 'string'.to_sym
|
12
|
+
# :symbol.to_sym
|
13
|
+
# 'underscored_string'.to_sym
|
14
|
+
# :'underscored_symbol'
|
15
|
+
# 'hyphenated-string'.to_sym
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# :string
|
19
|
+
# :symbol
|
20
|
+
# :underscored_string
|
21
|
+
# :underscored_symbol
|
22
|
+
# :'hyphenated-string'
|
23
|
+
#
|
24
|
+
class SymbolConversion < Base
|
25
|
+
extend AutoCorrector
|
26
|
+
|
27
|
+
MSG = 'Unnecessary symbol conversion; use `%<correction>s` instead.'
|
28
|
+
RESTRICT_ON_SEND = %i[to_sym intern].freeze
|
29
|
+
|
30
|
+
def on_send(node)
|
31
|
+
return unless node.receiver.str_type? || node.receiver.sym_type?
|
32
|
+
|
33
|
+
register_offense(node, correction: node.receiver.value.to_sym.inspect)
|
34
|
+
end
|
35
|
+
|
36
|
+
def on_sym(node)
|
37
|
+
return if properly_quoted?(node.source, node.value.inspect)
|
38
|
+
|
39
|
+
# `alias` arguments are symbols but since a symbol that requires
|
40
|
+
# being quoted is not a valid method identifier, it can be ignored
|
41
|
+
return if in_alias?(node)
|
42
|
+
|
43
|
+
# The `%I[]` and `%i[]` macros are parsed as normal arrays of symbols
|
44
|
+
# so they need to be ignored.
|
45
|
+
return if in_percent_literal_array?(node)
|
46
|
+
|
47
|
+
# Symbol hash keys have a different format and need to be handled separately
|
48
|
+
return correct_hash_key(node) if hash_key?(node)
|
49
|
+
|
50
|
+
register_offense(node, correction: node.value.inspect)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def register_offense(node, correction:, message: format(MSG, correction: correction))
|
56
|
+
add_offense(node, message: message) do |corrector|
|
57
|
+
corrector.replace(node, correction)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def properly_quoted?(source, value)
|
62
|
+
return true unless source.match?(/['"]/)
|
63
|
+
|
64
|
+
source == value ||
|
65
|
+
# `Symbol#inspect` uses double quotes, but allow single-quoted
|
66
|
+
# symbols to work as well.
|
67
|
+
source.tr("'", '"') == value
|
68
|
+
end
|
69
|
+
|
70
|
+
def in_alias?(node)
|
71
|
+
node.parent&.alias_type?
|
72
|
+
end
|
73
|
+
|
74
|
+
def in_percent_literal_array?(node)
|
75
|
+
node.parent&.array_type? && node.parent&.percent_literal?
|
76
|
+
end
|
77
|
+
|
78
|
+
def hash_key?(node)
|
79
|
+
node.parent&.pair_type? && node == node.parent.child_nodes.first
|
80
|
+
end
|
81
|
+
|
82
|
+
def correct_hash_key(node)
|
83
|
+
# Although some operators can be converted to symbols normally
|
84
|
+
# (ie. `:==`), these are not accepted as hash keys and will
|
85
|
+
# raise a syntax error (eg. `{ ==: ... }`). Therefore, if the
|
86
|
+
# symbol does not start with an alpha-numeric or underscore, it
|
87
|
+
# will be ignored.
|
88
|
+
return unless node.value.to_s.match?(/\A[a-z0-9_]/i)
|
89
|
+
|
90
|
+
correction = node.value.inspect.delete(':')
|
91
|
+
return if properly_quoted?(node.source, correction)
|
92
|
+
|
93
|
+
register_offense(
|
94
|
+
node,
|
95
|
+
correction: correction,
|
96
|
+
message: format(MSG, correction: "#{correction}:")
|
97
|
+
)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# This cop checks for "triple quotes" (strings delimted by any odd number
|
7
|
+
# of quotes greater than 1).
|
8
|
+
#
|
9
|
+
# Ruby allows multiple strings to be implicitly concatenated by just
|
10
|
+
# being adjacent in a statement (ie. `"foo""bar" == "foobar"`). This sometimes
|
11
|
+
# gives the impression that there is something special about triple quotes, but
|
12
|
+
# in fact it is just extra unnecessary quotes and produces the same string. Each
|
13
|
+
# pair of quotes produces an additional concatenated empty string, so the result
|
14
|
+
# is still only the "actual" string within the delimiters.
|
15
|
+
#
|
16
|
+
# NOTE: Although this cop is called triple quotes, the same behavior is present
|
17
|
+
# for strings delimited by 5, 7, etc. quotation marks.
|
18
|
+
#
|
19
|
+
# @example
|
20
|
+
# # bad
|
21
|
+
# """
|
22
|
+
# A string
|
23
|
+
# """
|
24
|
+
#
|
25
|
+
# # bad
|
26
|
+
# '''
|
27
|
+
# A string
|
28
|
+
# '''
|
29
|
+
#
|
30
|
+
# # good
|
31
|
+
# "
|
32
|
+
# A string
|
33
|
+
# "
|
34
|
+
#
|
35
|
+
# # good
|
36
|
+
# <<STRING
|
37
|
+
# A string
|
38
|
+
# STRING
|
39
|
+
#
|
40
|
+
# # good (but not the same spacing as the bad case)
|
41
|
+
# 'A string'
|
42
|
+
class TripleQuotes < Base
|
43
|
+
extend AutoCorrector
|
44
|
+
|
45
|
+
MSG = 'Delimiting a string with multiple quotes has no effect, use a single quote instead.'
|
46
|
+
|
47
|
+
def on_dstr(node)
|
48
|
+
return if (empty_str_nodes = empty_str_nodes(node)).none?
|
49
|
+
|
50
|
+
opening_quotes = node.source.scan(/(?<=\A)['"]*/)[0]
|
51
|
+
return if opening_quotes.size < 3
|
52
|
+
|
53
|
+
# If the node is composed of only empty `str` nodes, keep one
|
54
|
+
empty_str_nodes.shift if empty_str_nodes.size == node.child_nodes.size
|
55
|
+
|
56
|
+
add_offense(node) do |corrector|
|
57
|
+
empty_str_nodes.each do |str|
|
58
|
+
corrector.remove(str)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def empty_str_nodes(node)
|
66
|
+
node.each_child_node(:str).select { |str| str.value == '' }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|