summaryrefslogtreecommitdiff
path: root/rubocop/cop/line_break_after_guard_clauses.rb
blob: 67477f064ab2b3f86812cff0e3a0128bc6ed589f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# frozen_string_literal: true

module RuboCop
  module Cop
    # Ensures a line break after guard clauses.
    #
    # @example
    #   # bad
    #   return unless condition
    #   do_stuff
    #
    #   # good
    #   return unless condition
    #
    #   do_stuff
    #
    #   # bad
    #   raise if condition
    #   do_stuff
    #
    #   # good
    #   raise if condition
    #
    #   do_stuff
    #
    #   Multiple guard clauses are allowed without
    #   line break.
    #
    #   # good
    #   return unless condition_a
    #   return unless condition_b
    #
    #   do_stuff
    #
    #   Guard clauses in case statement are allowed without
    #   line break.
    #
    #   # good
    #   case model
    #     when condition_a
    #       return true unless condition_b
    #     when
    #       ...
    #   end
    #
    #   Guard clauses before end are allowed without
    #   line break.
    #
    #   # good
    #   if condition_a
    #     do_something
    #   else
    #     do_something_else
    #     return unless condition
    #   end
    #
    #   do_something_more
    class LineBreakAfterGuardClauses < RuboCop::Cop::Cop
      MSG = 'Add a line break after guard clauses'

      def_node_matcher :guard_clause_node?, <<-PATTERN
        [{(send nil? {:raise :fail :throw} ...) return break next} single_line?]
      PATTERN

      def on_if(node)
        return unless node.single_line?
        return unless guard_clause?(node)
        return if next_line(node).blank? || clause_last_line?(next_line(node)) || guard_clause?(next_sibling(node))

        add_offense(node, :expression, MSG)
      end

      def autocorrect(node)
        lambda do |corrector|
          corrector.insert_after(node.loc.expression, "\n")
        end
      end

      private

      def guard_clause?(node)
        return false unless node.if_type?

        guard_clause_node?(node.if_branch)
      end

      def next_sibling(node)
        node.parent.children[node.sibling_index + 1]
      end

      def next_line(node)
        processed_source[node.loc.line]
      end

      def clause_last_line?(line)
        line =~ /^\s*(?:end|elsif|else|when|rescue|ensure)/
      end
    end
  end
end