rubocop 1.0.0 → 1.4.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.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +36 -16
  3. data/config/default.yml +165 -19
  4. data/exe/rubocop +1 -1
  5. data/lib/rubocop.rb +17 -0
  6. data/lib/rubocop/cli/command/auto_genenerate_config.rb +1 -1
  7. data/lib/rubocop/cli/command/execute_runner.rb +26 -11
  8. data/lib/rubocop/comment_config.rb +1 -1
  9. data/lib/rubocop/config_loader.rb +14 -5
  10. data/lib/rubocop/config_regeneration.rb +1 -1
  11. data/lib/rubocop/cop/bundler/duplicated_gem.rb +26 -6
  12. data/lib/rubocop/cop/bundler/gem_comment.rb +1 -1
  13. data/lib/rubocop/cop/commissioner.rb +10 -10
  14. data/lib/rubocop/cop/corrector.rb +3 -1
  15. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +1 -1
  16. data/lib/rubocop/cop/force.rb +1 -1
  17. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +3 -3
  18. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +4 -5
  19. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +1 -1
  20. data/lib/rubocop/cop/generator.rb +2 -9
  21. data/lib/rubocop/cop/generator/configuration_injector.rb +1 -1
  22. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +1 -1
  23. data/lib/rubocop/cop/layout/block_alignment.rb +3 -4
  24. data/lib/rubocop/cop/layout/class_structure.rb +15 -3
  25. data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
  26. data/lib/rubocop/cop/layout/else_alignment.rb +15 -2
  27. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +77 -7
  28. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -0
  29. data/lib/rubocop/cop/layout/end_alignment.rb +3 -3
  30. data/lib/rubocop/cop/layout/extra_spacing.rb +1 -2
  31. data/lib/rubocop/cop/layout/hash_alignment.rb +4 -4
  32. data/lib/rubocop/cop/layout/line_length.rb +8 -1
  33. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +24 -18
  34. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +1 -1
  35. data/lib/rubocop/cop/layout/space_inside_parens.rb +35 -13
  36. data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -1
  37. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +2 -1
  38. data/lib/rubocop/cop/lint/constant_definition_in_block.rb +26 -2
  39. data/lib/rubocop/cop/lint/debugger.rb +17 -28
  40. data/lib/rubocop/cop/lint/duplicate_branch.rb +93 -0
  41. data/lib/rubocop/cop/lint/duplicate_case_condition.rb +2 -12
  42. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +77 -0
  43. data/lib/rubocop/cop/lint/else_layout.rb +29 -3
  44. data/lib/rubocop/cop/lint/empty_block.rb +82 -0
  45. data/lib/rubocop/cop/lint/empty_class.rb +93 -0
  46. data/lib/rubocop/cop/lint/flip_flop.rb +8 -2
  47. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +39 -7
  48. data/lib/rubocop/cop/lint/loop.rb +4 -4
  49. data/lib/rubocop/cop/lint/missing_super.rb +7 -4
  50. data/lib/rubocop/cop/lint/nested_percent_literal.rb +14 -0
  51. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +58 -0
  52. data/lib/rubocop/cop/lint/number_conversion.rb +46 -13
  53. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +27 -8
  54. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +19 -16
  55. data/lib/rubocop/cop/lint/shadowed_exception.rb +4 -5
  56. data/lib/rubocop/cop/lint/to_enum_arguments.rb +86 -0
  57. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +194 -0
  58. data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
  59. data/lib/rubocop/cop/lint/useless_method_definition.rb +2 -4
  60. data/lib/rubocop/cop/lint/useless_setter_call.rb +6 -1
  61. data/lib/rubocop/cop/metrics/method_length.rb +1 -1
  62. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  63. data/lib/rubocop/cop/mixin/configurable_numbering.rb +3 -3
  64. data/lib/rubocop/cop/mixin/line_length_help.rb +1 -1
  65. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
  66. data/lib/rubocop/cop/mixin/statement_modifier.rb +9 -4
  67. data/lib/rubocop/cop/mixin/visibility_help.rb +1 -3
  68. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +11 -1
  69. data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +11 -5
  70. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +67 -18
  71. data/lib/rubocop/cop/naming/predicate_name.rb +2 -1
  72. data/lib/rubocop/cop/naming/variable_number.rb +98 -8
  73. data/lib/rubocop/cop/offense.rb +3 -3
  74. data/lib/rubocop/cop/style/and_or.rb +1 -3
  75. data/lib/rubocop/cop/style/arguments_forwarding.rb +142 -0
  76. data/lib/rubocop/cop/style/bisected_attr_accessor.rb +0 -4
  77. data/lib/rubocop/cop/style/case_like_if.rb +0 -4
  78. data/lib/rubocop/cop/style/collection_compact.rb +91 -0
  79. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +169 -0
  80. data/lib/rubocop/cop/style/documentation.rb +12 -1
  81. data/lib/rubocop/cop/style/double_negation.rb +6 -1
  82. data/lib/rubocop/cop/style/hash_syntax.rb +3 -3
  83. data/lib/rubocop/cop/style/identical_conditional_branches.rb +7 -2
  84. data/lib/rubocop/cop/style/if_inside_else.rb +37 -1
  85. data/lib/rubocop/cop/style/if_unless_modifier.rb +7 -3
  86. data/lib/rubocop/cop/style/infinite_loop.rb +4 -0
  87. data/lib/rubocop/cop/style/keyword_parameters_order.rb +12 -0
  88. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -2
  89. data/lib/rubocop/cop/style/mixin_grouping.rb +0 -4
  90. data/lib/rubocop/cop/style/multiple_comparison.rb +55 -7
  91. data/lib/rubocop/cop/style/negated_if_else_condition.rb +106 -0
  92. data/lib/rubocop/cop/style/nil_lambda.rb +52 -0
  93. data/lib/rubocop/cop/style/raise_args.rb +21 -6
  94. data/lib/rubocop/cop/style/redundant_argument.rb +73 -0
  95. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +7 -1
  96. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +1 -1
  97. data/lib/rubocop/cop/style/semicolon.rb +3 -0
  98. data/lib/rubocop/cop/style/static_class.rb +97 -0
  99. data/lib/rubocop/cop/style/swap_values.rb +108 -0
  100. data/lib/rubocop/cop/style/while_until_modifier.rb +9 -0
  101. data/lib/rubocop/cop/team.rb +6 -1
  102. data/lib/rubocop/cop/util.rb +6 -2
  103. data/lib/rubocop/cop/variable_force/branch.rb +1 -1
  104. data/lib/rubocop/cop/variable_force/scope.rb +1 -1
  105. data/lib/rubocop/ext/regexp_node.rb +17 -9
  106. data/lib/rubocop/ext/regexp_parser.rb +84 -0
  107. data/lib/rubocop/formatter/disabled_config_formatter.rb +21 -6
  108. data/lib/rubocop/formatter/formatter_set.rb +2 -1
  109. data/lib/rubocop/formatter/git_hub_actions_formatter.rb +47 -0
  110. data/lib/rubocop/magic_comment.rb +2 -2
  111. data/lib/rubocop/options.rb +7 -0
  112. data/lib/rubocop/rake_task.rb +2 -2
  113. data/lib/rubocop/rspec/shared_contexts.rb +4 -0
  114. data/lib/rubocop/runner.rb +1 -1
  115. data/lib/rubocop/target_finder.rb +1 -1
  116. data/lib/rubocop/target_ruby.rb +65 -1
  117. data/lib/rubocop/version.rb +1 -1
  118. metadata +22 -10
  119. data/assets/logo.png +0 -0
  120. data/assets/output.html.erb +0 -261
  121. data/bin/console +0 -10
  122. data/bin/rubocop-profile +0 -32
  123. data/bin/setup +0 -7
@@ -51,7 +51,7 @@ module RuboCop
51
51
  alias on_csend on_send
52
52
 
53
53
  def on_const(node)
54
- return unless node.loc.double_colon
54
+ return unless node.loc.respond_to?(:double_colon) && node.loc.double_colon
55
55
 
56
56
  check_space_after_double_colon(node)
57
57
  end
@@ -11,10 +11,12 @@ module RuboCop
11
11
  # # bad
12
12
  # f( 3)
13
13
  # g = (a + 3 )
14
+ # f( )
14
15
  #
15
16
  # # good
16
17
  # f(3)
17
18
  # g = (a + 3)
19
+ # f()
18
20
  #
19
21
  # @example EnforcedStyle: space
20
22
  # # The `space` style enforces that parentheses have a space at the
@@ -44,11 +46,7 @@ module RuboCop
44
46
  @processed_source = processed_source
45
47
 
46
48
  if style == :space
47
- each_missing_space(processed_source.tokens) do |range|
48
- add_offense(range, message: MSG_SPACE) do |corrector|
49
- corrector.insert_before(range, ' ')
50
- end
51
- end
49
+ process_with_space_style(processed_source)
52
50
  else
53
51
  each_extraneous_space(processed_source.tokens) do |range|
54
52
  add_offense(range) do |corrector|
@@ -60,6 +58,21 @@ module RuboCop
60
58
 
61
59
  private
62
60
 
61
+ def process_with_space_style(processed_source)
62
+ processed_source.tokens.each_cons(2) do |token1, token2|
63
+ each_extraneous_space_in_empty_parens(token1, token2) do |range|
64
+ add_offense(range) do |corrector|
65
+ corrector.remove(range)
66
+ end
67
+ end
68
+ each_missing_space(token1, token2) do |range|
69
+ add_offense(range, message: MSG_SPACE) do |corrector|
70
+ corrector.insert_before(range, ' ')
71
+ end
72
+ end
73
+ end
74
+ end
75
+
63
76
  def each_extraneous_space(tokens)
64
77
  tokens.each_cons(2) do |token1, token2|
65
78
  next unless parens?(token1, token2)
@@ -73,15 +86,21 @@ module RuboCop
73
86
  end
74
87
  end
75
88
 
76
- def each_missing_space(tokens)
77
- tokens.each_cons(2) do |token1, token2|
78
- next if can_be_ignored?(token1, token2)
89
+ def each_extraneous_space_in_empty_parens(token1, token2)
90
+ return unless token1.left_parens? && token2.right_parens?
79
91
 
80
- if token1.left_parens?
81
- yield range_between(token2.begin_pos, token2.begin_pos + 1)
82
- elsif token2.right_parens?
83
- yield range_between(token2.begin_pos, token2.end_pos)
84
- end
92
+ return if range_between(token1.begin_pos, token2.end_pos).source == '()'
93
+
94
+ yield range_between(token1.end_pos, token2.begin_pos)
95
+ end
96
+
97
+ def each_missing_space(token1, token2)
98
+ return if can_be_ignored?(token1, token2)
99
+
100
+ if token1.left_parens?
101
+ yield range_between(token2.begin_pos, token2.begin_pos + 1)
102
+ elsif token2.right_parens?
103
+ yield range_between(token2.begin_pos, token2.end_pos)
85
104
  end
86
105
  end
87
106
 
@@ -96,6 +115,9 @@ module RuboCop
96
115
  def can_be_ignored?(token1, token2)
97
116
  return true unless parens?(token1, token2)
98
117
 
118
+ # Ignore empty parentheses.
119
+ return true if range_between(token1.begin_pos, token2.end_pos).source == '()'
120
+
99
121
  # If the second token is a comment, that means that a line break
100
122
  # follows, and that the rules for space inside don't apply.
101
123
  return true if token2.comment?
@@ -63,7 +63,7 @@ module RuboCop
63
63
  range = offense_range(lineno, line)
64
64
  add_offense(range) do |corrector|
65
65
  if heredoc
66
- corrector.insert_after(range, '#{}') unless static?(heredoc) # rubocop:disable Lint/InterpolationCheck
66
+ corrector.wrap(range, "\#{'", "'}") unless static?(heredoc)
67
67
  else
68
68
  corrector.remove(range)
69
69
  end
@@ -53,7 +53,8 @@ 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, regexp_receiver)
56
+ if (node.parent.send_type? && node.receiver) ||
57
+ method_chain_to_regexp_receiver?(node, regexp_receiver)
57
58
  node = find_offense_node(node.parent, regexp_receiver)
58
59
  end
59
60
 
@@ -49,7 +49,21 @@ module RuboCop
49
49
  # const_set(:LIST, [])
50
50
  # end
51
51
  # end
52
+ #
53
+ # @example AllowedMethods: ['enums'] (default)
54
+ # # good
55
+ #
56
+ # # `enums` for Typed Enums via `T::Enum` in Sorbet.
57
+ # # https://sorbet.org/docs/tenum
58
+ # class TestEnum < T::Enum
59
+ # enums do
60
+ # Foo = new("foo")
61
+ # end
62
+ # end
63
+ #
52
64
  class ConstantDefinitionInBlock < Base
65
+ include AllowedMethods
66
+
53
67
  MSG = 'Do not define constants this way within a block.'
54
68
 
55
69
  def_node_matcher :constant_assigned_in_block?, <<~PATTERN
@@ -61,13 +75,23 @@ module RuboCop
61
75
  PATTERN
62
76
 
63
77
  def on_casgn(node)
64
- add_offense(node) if constant_assigned_in_block?(node)
78
+ return if !constant_assigned_in_block?(node) || allowed_method?(method_name(node))
79
+
80
+ add_offense(node)
65
81
  end
66
82
 
67
83
  def on_class(node)
68
- add_offense(node) if module_defined_in_block?(node)
84
+ return if !module_defined_in_block?(node) || allowed_method?(method_name(node))
85
+
86
+ add_offense(node)
69
87
  end
70
88
  alias on_module on_class
89
+
90
+ private
91
+
92
+ def method_name(node)
93
+ node.ancestors.find(&:block_type?).send_node.method_name
94
+ end
71
95
  end
72
96
  end
73
97
  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 = %i[
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 debugger_call?(node) || binding_irb?(node)
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 binding_irb?(node)
77
- binding_irb_call?(node)
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([]) do |when_node, previous|
34
+ case_node.when_branches.each_with_object(Set.new) do |when_node, previous|
35
35
  when_node.each_condition do |condition|
36
- next unless repeated_condition?(previous, condition)
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