rubocop 1.6.0 → 1.9.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/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
|