rubocop 0.93.0 → 1.3.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 +36 -16
- data/config/default.yml +207 -77
- data/exe/rubocop +1 -1
- data/lib/rubocop.rb +16 -2
- data/lib/rubocop/cli/command/auto_genenerate_config.rb +1 -1
- data/lib/rubocop/cli/command/version.rb +1 -1
- data/lib/rubocop/comment_config.rb +1 -1
- data/lib/rubocop/config.rb +4 -0
- data/lib/rubocop/config_loader.rb +26 -8
- data/lib/rubocop/config_loader_resolver.rb +7 -5
- data/lib/rubocop/config_validator.rb +7 -6
- data/lib/rubocop/cop/badge.rb +9 -24
- data/lib/rubocop/cop/base.rb +16 -1
- data/lib/rubocop/cop/bundler/duplicated_gem.rb +26 -6
- data/lib/rubocop/cop/bundler/gem_comment.rb +1 -1
- data/lib/rubocop/cop/commissioner.rb +37 -23
- data/lib/rubocop/cop/corrector.rb +3 -1
- data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +1 -1
- data/lib/rubocop/cop/force.rb +1 -1
- data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +3 -3
- data/lib/rubocop/cop/gemspec/required_ruby_version.rb +4 -5
- data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +1 -1
- data/lib/rubocop/cop/generator.rb +1 -1
- data/lib/rubocop/cop/layout/block_alignment.rb +3 -4
- data/lib/rubocop/cop/layout/class_structure.rb +7 -0
- data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/else_alignment.rb +15 -2
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -0
- data/lib/rubocop/cop/layout/end_alignment.rb +3 -3
- data/lib/rubocop/cop/layout/extra_spacing.rb +1 -2
- data/lib/rubocop/cop/layout/hash_alignment.rb +4 -4
- data/lib/rubocop/cop/layout/line_length.rb +8 -1
- data/lib/rubocop/cop/layout/space_around_block_parameters.rb +24 -18
- data/lib/rubocop/cop/layout/space_around_operators.rb +4 -1
- data/lib/rubocop/cop/layout/space_inside_parens.rb +35 -13
- data/lib/rubocop/cop/layout/trailing_whitespace.rb +37 -13
- data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +6 -3
- data/lib/rubocop/cop/lint/constant_definition_in_block.rb +23 -2
- data/lib/rubocop/cop/lint/debugger.rb +17 -28
- data/lib/rubocop/cop/lint/duplicate_branch.rb +93 -0
- data/lib/rubocop/cop/lint/duplicate_case_condition.rb +2 -12
- data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +77 -0
- data/lib/rubocop/cop/lint/else_layout.rb +29 -3
- data/lib/rubocop/cop/lint/empty_block.rb +82 -0
- data/lib/rubocop/cop/lint/empty_class.rb +93 -0
- data/lib/rubocop/cop/lint/flip_flop.rb +8 -2
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +38 -6
- data/lib/rubocop/cop/lint/loop.rb +4 -4
- data/lib/rubocop/cop/lint/nested_percent_literal.rb +14 -0
- data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +58 -0
- data/lib/rubocop/cop/lint/number_conversion.rb +46 -13
- data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +27 -8
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +19 -16
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +44 -11
- data/lib/rubocop/cop/lint/shadowed_exception.rb +4 -5
- data/lib/rubocop/cop/lint/to_enum_arguments.rb +95 -0
- data/lib/rubocop/cop/lint/to_json.rb +1 -1
- data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +185 -0
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
- data/lib/rubocop/cop/lint/useless_method_definition.rb +2 -4
- data/lib/rubocop/cop/lint/useless_setter_call.rb +6 -1
- data/lib/rubocop/cop/metrics/class_length.rb +9 -3
- data/lib/rubocop/cop/metrics/parameter_lists.rb +4 -1
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
- data/lib/rubocop/cop/mixin/configurable_numbering.rb +3 -3
- data/lib/rubocop/cop/mixin/line_length_help.rb +1 -1
- data/lib/rubocop/cop/mixin/statement_modifier.rb +9 -4
- data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +12 -2
- data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +11 -5
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +67 -18
- data/lib/rubocop/cop/naming/predicate_name.rb +2 -1
- data/lib/rubocop/cop/naming/variable_number.rb +98 -8
- data/lib/rubocop/cop/offense.rb +3 -3
- data/lib/rubocop/cop/security/open.rb +12 -10
- data/lib/rubocop/cop/style/accessor_grouping.rb +1 -1
- data/lib/rubocop/cop/style/and_or.rb +1 -3
- data/lib/rubocop/cop/style/arguments_forwarding.rb +142 -0
- data/lib/rubocop/cop/style/bisected_attr_accessor.rb +0 -4
- data/lib/rubocop/cop/style/case_like_if.rb +0 -4
- data/lib/rubocop/cop/style/class_equality_comparison.rb +19 -4
- data/lib/rubocop/cop/style/collection_compact.rb +91 -0
- data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +162 -0
- data/lib/rubocop/cop/style/double_negation.rb +6 -1
- data/lib/rubocop/cop/style/format_string_token.rb +47 -2
- data/lib/rubocop/cop/style/hash_syntax.rb +3 -3
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +7 -2
- data/lib/rubocop/cop/style/if_inside_else.rb +37 -1
- data/lib/rubocop/cop/style/if_unless_modifier.rb +7 -3
- data/lib/rubocop/cop/style/infinite_loop.rb +4 -0
- data/lib/rubocop/cop/style/keyword_parameters_order.rb +12 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +10 -13
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +6 -11
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +7 -11
- data/lib/rubocop/cop/style/mixin_grouping.rb +0 -4
- data/lib/rubocop/cop/style/multiple_comparison.rb +55 -7
- data/lib/rubocop/cop/style/negated_if_else_condition.rb +104 -0
- data/lib/rubocop/cop/style/nil_lambda.rb +52 -0
- data/lib/rubocop/cop/style/raise_args.rb +21 -6
- data/lib/rubocop/cop/style/redundant_begin.rb +14 -4
- data/lib/rubocop/cop/style/redundant_parentheses.rb +4 -0
- data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +7 -1
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +1 -1
- data/lib/rubocop/cop/style/redundant_self.rb +3 -0
- data/lib/rubocop/cop/style/safe_navigation.rb +16 -4
- data/lib/rubocop/cop/style/semicolon.rb +3 -0
- data/lib/rubocop/cop/style/static_class.rb +97 -0
- data/lib/rubocop/cop/style/string_concatenation.rb +13 -1
- data/lib/rubocop/cop/style/swap_values.rb +108 -0
- data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/trailing_underscore_variable.rb +3 -1
- data/lib/rubocop/cop/style/while_until_modifier.rb +9 -0
- data/lib/rubocop/cop/team.rb +6 -1
- data/lib/rubocop/cop/util.rb +5 -1
- data/lib/rubocop/ext/regexp_node.rb +17 -9
- data/lib/rubocop/ext/regexp_parser.rb +84 -0
- data/lib/rubocop/formatter/formatter_set.rb +2 -1
- data/lib/rubocop/formatter/git_hub_actions_formatter.rb +47 -0
- data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
- data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
- data/lib/rubocop/magic_comment.rb +2 -2
- data/lib/rubocop/options.rb +6 -1
- data/lib/rubocop/rspec/shared_contexts.rb +4 -0
- data/lib/rubocop/target_ruby.rb +57 -1
- data/lib/rubocop/version.rb +56 -6
- metadata +21 -5
@@ -14,14 +14,25 @@ module RuboCop
|
|
14
14
|
# # good
|
15
15
|
# x = 0
|
16
16
|
#
|
17
|
-
# @example AllowInHeredoc: false
|
17
|
+
# @example AllowInHeredoc: false (default)
|
18
18
|
# # The line in this example contains spaces after the 0.
|
19
19
|
# # bad
|
20
20
|
# code = <<~RUBY
|
21
21
|
# x = 0
|
22
22
|
# RUBY
|
23
23
|
#
|
24
|
-
#
|
24
|
+
# # ok
|
25
|
+
# code = <<~RUBY
|
26
|
+
# x = 0 #{}
|
27
|
+
# RUBY
|
28
|
+
#
|
29
|
+
# # good
|
30
|
+
# trailing_whitespace = ' '
|
31
|
+
# code = <<~RUBY
|
32
|
+
# x = 0#{trailing_whitespace}
|
33
|
+
# RUBY
|
34
|
+
#
|
35
|
+
# @example AllowInHeredoc: true
|
25
36
|
# # The line in this example contains spaces after the 0.
|
26
37
|
# # good
|
27
38
|
# code = <<~RUBY
|
@@ -35,36 +46,49 @@ module RuboCop
|
|
35
46
|
MSG = 'Trailing whitespace detected.'
|
36
47
|
|
37
48
|
def on_new_investigation
|
38
|
-
|
49
|
+
@heredocs = extract_heredocs(processed_source.ast)
|
39
50
|
processed_source.lines.each_with_index do |line, index|
|
40
|
-
lineno = index + 1
|
41
|
-
|
42
51
|
next unless line.end_with?(' ', "\t")
|
43
|
-
next if skip_heredoc? && inside_heredoc?(heredoc_ranges, lineno)
|
44
52
|
|
45
|
-
|
46
|
-
|
53
|
+
process_line(line, index + 1)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def process_line(line, lineno)
|
60
|
+
heredoc = find_heredoc(lineno)
|
61
|
+
return if skip_heredoc? && heredoc
|
62
|
+
|
63
|
+
range = offense_range(lineno, line)
|
64
|
+
add_offense(range) do |corrector|
|
65
|
+
if heredoc
|
66
|
+
corrector.wrap(range, "\#{'", "'}") unless static?(heredoc)
|
67
|
+
else
|
47
68
|
corrector.remove(range)
|
48
69
|
end
|
49
70
|
end
|
50
71
|
end
|
51
72
|
|
52
|
-
|
73
|
+
def static?(heredoc)
|
74
|
+
heredoc.loc.expression.source.end_with? "'"
|
75
|
+
end
|
53
76
|
|
54
77
|
def skip_heredoc?
|
55
78
|
cop_config.fetch('AllowInHeredoc', false)
|
56
79
|
end
|
57
80
|
|
58
|
-
def
|
59
|
-
|
81
|
+
def find_heredoc(line_number)
|
82
|
+
@heredocs.each { |node, r| return node if r.include?(line_number) }
|
83
|
+
nil
|
60
84
|
end
|
61
85
|
|
62
|
-
def
|
86
|
+
def extract_heredocs(ast)
|
63
87
|
return [] unless ast
|
64
88
|
|
65
89
|
ast.each_node(:str, :dstr, :xstr).select(&:heredoc?).map do |node|
|
66
90
|
body = node.location.heredoc_body
|
67
|
-
|
91
|
+
[node, body.first_line...body.last_line]
|
68
92
|
end
|
69
93
|
end
|
70
94
|
|
@@ -53,15 +53,18 @@ module RuboCop
|
|
53
53
|
def find_offense_node(node, regexp_receiver)
|
54
54
|
return node unless node.parent
|
55
55
|
|
56
|
-
if node.parent.send_type? || method_chain_to_regexp_receiver?(node)
|
56
|
+
if node.parent.send_type? || method_chain_to_regexp_receiver?(node, regexp_receiver)
|
57
57
|
node = find_offense_node(node.parent, regexp_receiver)
|
58
58
|
end
|
59
59
|
|
60
60
|
node
|
61
61
|
end
|
62
62
|
|
63
|
-
def method_chain_to_regexp_receiver?(node)
|
64
|
-
|
63
|
+
def method_chain_to_regexp_receiver?(node, regexp_receiver)
|
64
|
+
return false unless (parent = node.parent)
|
65
|
+
return false unless (parent_receiver = parent.receiver)
|
66
|
+
|
67
|
+
parent.parent && parent_receiver.receiver == regexp_receiver
|
65
68
|
end
|
66
69
|
end
|
67
70
|
end
|
@@ -49,7 +49,18 @@ module RuboCop
|
|
49
49
|
# const_set(:LIST, [])
|
50
50
|
# end
|
51
51
|
# end
|
52
|
+
#
|
53
|
+
# @example AllowedMethods: ['enums']
|
54
|
+
# # good
|
55
|
+
# class TestEnum < T::Enum
|
56
|
+
# enums do
|
57
|
+
# Foo = new("foo")
|
58
|
+
# end
|
59
|
+
# end
|
60
|
+
#
|
52
61
|
class ConstantDefinitionInBlock < Base
|
62
|
+
include AllowedMethods
|
63
|
+
|
53
64
|
MSG = 'Do not define constants this way within a block.'
|
54
65
|
|
55
66
|
def_node_matcher :constant_assigned_in_block?, <<~PATTERN
|
@@ -61,13 +72,23 @@ module RuboCop
|
|
61
72
|
PATTERN
|
62
73
|
|
63
74
|
def on_casgn(node)
|
64
|
-
|
75
|
+
return if !constant_assigned_in_block?(node) || allowed_method?(method_name(node))
|
76
|
+
|
77
|
+
add_offense(node)
|
65
78
|
end
|
66
79
|
|
67
80
|
def on_class(node)
|
68
|
-
|
81
|
+
return if !module_defined_in_block?(node) || allowed_method?(method_name(node))
|
82
|
+
|
83
|
+
add_offense(node)
|
69
84
|
end
|
70
85
|
alias on_module on_class
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def method_name(node)
|
90
|
+
node.ancestors.find(&:block_type?).send_node.method_name
|
91
|
+
end
|
71
92
|
end
|
72
93
|
end
|
73
94
|
end
|
@@ -4,6 +4,7 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module Lint
|
6
6
|
# This cop checks for calls to debugger or pry.
|
7
|
+
# The cop can be configured to define which methods and receivers must be fixed.
|
7
8
|
#
|
8
9
|
# @example
|
9
10
|
#
|
@@ -35,34 +36,11 @@ module RuboCop
|
|
35
36
|
class Debugger < Base
|
36
37
|
MSG = 'Remove debugger entry point `%<source>s`.'
|
37
38
|
|
38
|
-
RESTRICT_ON_SEND =
|
39
|
-
debugger byebug remote_byebug pry remote_pry pry_remote console rescue
|
40
|
-
save_and_open_page save_and_open_screenshot save_screenshot irb
|
41
|
-
].freeze
|
42
|
-
|
43
|
-
def_node_matcher :kernel?, <<~PATTERN
|
44
|
-
{
|
45
|
-
(const nil? :Kernel)
|
46
|
-
(const (cbase) :Kernel)
|
47
|
-
}
|
48
|
-
PATTERN
|
49
|
-
|
50
|
-
def_node_matcher :debugger_call?, <<~PATTERN
|
51
|
-
{(send {nil? #kernel?} {:debugger :byebug :remote_byebug} ...)
|
52
|
-
(send (send {#kernel? nil?} :binding)
|
53
|
-
{:pry :remote_pry :pry_remote :console} ...)
|
54
|
-
(send (const {nil? (cbase)} :Pry) :rescue ...)
|
55
|
-
(send nil? {:save_and_open_page
|
56
|
-
:save_and_open_screenshot
|
57
|
-
:save_screenshot} ...)}
|
58
|
-
PATTERN
|
59
|
-
|
60
|
-
def_node_matcher :binding_irb_call?, <<~PATTERN
|
61
|
-
(send (send {#kernel? nil?} :binding) :irb ...)
|
62
|
-
PATTERN
|
39
|
+
RESTRICT_ON_SEND = [].freeze
|
63
40
|
|
64
41
|
def on_send(node)
|
65
|
-
return unless
|
42
|
+
return unless debugger_method?(node.method_name)
|
43
|
+
return if !node.receiver.nil? && !debugger_receiver?(node)
|
66
44
|
|
67
45
|
add_offense(node)
|
68
46
|
end
|
@@ -73,8 +51,19 @@ module RuboCop
|
|
73
51
|
format(MSG, source: node.source)
|
74
52
|
end
|
75
53
|
|
76
|
-
def
|
77
|
-
|
54
|
+
def debugger_method?(name)
|
55
|
+
cop_config.fetch('DebuggerMethods', []).include?(name.to_s)
|
56
|
+
end
|
57
|
+
|
58
|
+
def debugger_receiver?(node)
|
59
|
+
receiver = case node.receiver
|
60
|
+
when RuboCop::AST::SendNode
|
61
|
+
node.receiver.method_name
|
62
|
+
when RuboCop::AST::ConstNode
|
63
|
+
node.receiver.const_name
|
64
|
+
end
|
65
|
+
|
66
|
+
cop_config.fetch('DebuggerReceivers', []).include?(receiver.to_s)
|
78
67
|
end
|
79
68
|
end
|
80
69
|
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# This cop checks that there are no repeated bodies
|
7
|
+
# within `if/unless`, `case-when` and `rescue` constructs.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# if foo
|
12
|
+
# do_foo
|
13
|
+
# do_something_else
|
14
|
+
# elsif bar
|
15
|
+
# do_foo
|
16
|
+
# do_something_else
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# # good
|
20
|
+
# if foo || bar
|
21
|
+
# do_foo
|
22
|
+
# do_something_else
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# # bad
|
26
|
+
# case x
|
27
|
+
# when foo
|
28
|
+
# do_foo
|
29
|
+
# when bar
|
30
|
+
# do_foo
|
31
|
+
# else
|
32
|
+
# do_something_else
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# # good
|
36
|
+
# case x
|
37
|
+
# when foo, bar
|
38
|
+
# do_foo
|
39
|
+
# else
|
40
|
+
# do_something_else
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# # bad
|
44
|
+
# begin
|
45
|
+
# do_something
|
46
|
+
# rescue FooError
|
47
|
+
# handle_error
|
48
|
+
# rescue BarError
|
49
|
+
# handle_error
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# # good
|
53
|
+
# begin
|
54
|
+
# do_something
|
55
|
+
# rescue FooError, BarError
|
56
|
+
# handle_error
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
class DuplicateBranch < Base
|
60
|
+
include RescueNode
|
61
|
+
|
62
|
+
MSG = 'Duplicate branch body detected.'
|
63
|
+
|
64
|
+
def on_branching_statement(node)
|
65
|
+
branches = node.branches.compact
|
66
|
+
branches.each_with_object(Set.new) do |branch, previous|
|
67
|
+
add_offense(offense_range(branch)) unless previous.add?(branch)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
alias on_if on_branching_statement
|
71
|
+
alias on_case on_branching_statement
|
72
|
+
alias on_rescue on_branching_statement
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def offense_range(duplicate_branch)
|
77
|
+
parent = duplicate_branch.parent
|
78
|
+
|
79
|
+
if parent.respond_to?(:else_branch) &&
|
80
|
+
parent.else_branch.equal?(duplicate_branch)
|
81
|
+
if parent.if_type? && parent.ternary?
|
82
|
+
duplicate_branch.source_range
|
83
|
+
else
|
84
|
+
parent.loc.else
|
85
|
+
end
|
86
|
+
else
|
87
|
+
parent.source_range
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -31,22 +31,12 @@ module RuboCop
|
|
31
31
|
MSG = 'Duplicate `when` condition detected.'
|
32
32
|
|
33
33
|
def on_case(case_node)
|
34
|
-
case_node.when_branches.each_with_object(
|
34
|
+
case_node.when_branches.each_with_object(Set.new) do |when_node, previous|
|
35
35
|
when_node.each_condition do |condition|
|
36
|
-
|
37
|
-
|
38
|
-
add_offense(condition)
|
36
|
+
add_offense(condition) unless previous.add?(condition)
|
39
37
|
end
|
40
|
-
|
41
|
-
previous.push(when_node.conditions)
|
42
38
|
end
|
43
39
|
end
|
44
|
-
|
45
|
-
private
|
46
|
-
|
47
|
-
def repeated_condition?(previous, condition)
|
48
|
-
previous.any? { |c| c.include?(condition) }
|
49
|
-
end
|
50
40
|
end
|
51
41
|
end
|
52
42
|
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# This cop checks for duplicate elements in Regexp character classes.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
#
|
10
|
+
# # bad
|
11
|
+
# r = /[xyx]/
|
12
|
+
#
|
13
|
+
# # bad
|
14
|
+
# r = /[0-9x0-9]/
|
15
|
+
#
|
16
|
+
# # good
|
17
|
+
# r = /[xy]/
|
18
|
+
#
|
19
|
+
# # good
|
20
|
+
# r = /[0-9x]/
|
21
|
+
class DuplicateRegexpCharacterClassElement < Base
|
22
|
+
include RangeHelp
|
23
|
+
extend AutoCorrector
|
24
|
+
|
25
|
+
MSG_REPEATED_ELEMENT = 'Duplicate element inside regexp character class'
|
26
|
+
|
27
|
+
def on_regexp(node)
|
28
|
+
each_repeated_character_class_element_loc(node) do |loc|
|
29
|
+
add_offense(loc, message: MSG_REPEATED_ELEMENT) do |corrector|
|
30
|
+
corrector.remove(loc)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def each_repeated_character_class_element_loc(node)
|
36
|
+
node.parsed_tree&.each_expression do |expr|
|
37
|
+
next if expr.type != :set || expr.token == :intersection
|
38
|
+
|
39
|
+
seen = Set.new
|
40
|
+
|
41
|
+
expr.expressions.each do |child|
|
42
|
+
next if within_interpolation?(node, child)
|
43
|
+
|
44
|
+
child_source = child.to_s
|
45
|
+
|
46
|
+
yield child.expression if seen.include?(child_source)
|
47
|
+
|
48
|
+
seen << child_source
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
# Since we blank interpolations with a space for every char of the interpolation, we would
|
56
|
+
# mark every space (except the first) as duplicate if we do not skip regexp_parser nodes
|
57
|
+
# that are within an interpolation.
|
58
|
+
def within_interpolation?(node, child)
|
59
|
+
parse_tree_child_loc = child.expression
|
60
|
+
|
61
|
+
interpolation_locs(node).any? { |il| il.overlaps?(parse_tree_child_loc) }
|
62
|
+
end
|
63
|
+
|
64
|
+
def interpolation_locs(node)
|
65
|
+
@interpolation_locs ||= {}
|
66
|
+
|
67
|
+
# Cache by loc, not by regexp content, as content can be repeated in multiple patterns
|
68
|
+
key = node.loc
|
69
|
+
|
70
|
+
@interpolation_locs[key] ||= node.children.select(&:begin_type?).map do |interpolation|
|
71
|
+
interpolation.loc.expression
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -3,10 +3,14 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Lint
|
6
|
-
# This cop checks for odd else block layout - like
|
7
|
-
# having an expression on the same line as the else keyword,
|
6
|
+
# This cop checks for odd `else` block layout - like
|
7
|
+
# having an expression on the same line as the `else` keyword,
|
8
8
|
# which is usually a mistake.
|
9
9
|
#
|
10
|
+
# Its auto-correction tweaks layout to keep the syntax. So, this auto-correction
|
11
|
+
# is compatible correction for bad case syntax, but if your code makes a mistake
|
12
|
+
# with `elsif` and `else`, you will have to correct it manually.
|
13
|
+
#
|
10
14
|
# @example
|
11
15
|
#
|
12
16
|
# # bad
|
@@ -21,13 +25,25 @@ module RuboCop
|
|
21
25
|
#
|
22
26
|
# # good
|
23
27
|
#
|
28
|
+
# # This code is compatible with the bad case. It will be auto-corrected like this.
|
24
29
|
# if something
|
25
30
|
# # ...
|
26
31
|
# else
|
27
32
|
# do_this
|
28
33
|
# do_that
|
29
34
|
# end
|
35
|
+
#
|
36
|
+
# # This code is incompatible with the bad case.
|
37
|
+
# # If `do_this` is a condition, `elsif` should be used instead of `else`.
|
38
|
+
# if something
|
39
|
+
# # ...
|
40
|
+
# elsif do_this
|
41
|
+
# do_that
|
42
|
+
# end
|
30
43
|
class ElseLayout < Base
|
44
|
+
include RangeHelp
|
45
|
+
extend AutoCorrector
|
46
|
+
|
31
47
|
MSG = 'Odd `else` layout detected. Did you mean to use `elsif`?'
|
32
48
|
|
33
49
|
def on_if(node)
|
@@ -58,7 +74,17 @@ module RuboCop
|
|
58
74
|
return unless first_else
|
59
75
|
return unless first_else.source_range.line == node.loc.else.line
|
60
76
|
|
61
|
-
add_offense(first_else)
|
77
|
+
add_offense(first_else) do |corrector|
|
78
|
+
autocorrect(corrector, node, first_else)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def autocorrect(corrector, node, first_else)
|
83
|
+
corrector.insert_after(node.loc.else, "\n")
|
84
|
+
|
85
|
+
blank_range = range_between(node.loc.else.end_pos, first_else.loc.expression.begin_pos)
|
86
|
+
indentation = indent(node.else_branch.children[1])
|
87
|
+
corrector.replace(blank_range, indentation)
|
62
88
|
end
|
63
89
|
end
|
64
90
|
end
|