Class: RuboCop::Cop::Sane::EmptyLinesAroundMultilineBlock

Inherits:
Base
  • Object
show all
Extended by:
AutoCorrector
Defined in:
lib/rubocop/cop/sane/empty_lines_around_multiline_block.rb

Overview

This cop enforces empty lines before and after multiline blocks such as ‘if/else` and `case/when`.

Examples:

# bad
work_for = data.work_done_for
if data.present?
  creation_date = date1
else
  creation_date = date2
end
legal_start_date = date3

# good
work_for = data.work_done_for

if data.present?
  creation_date = date1
else
  creation_date = date2
end

legal_start_date = date3

# good - no blank line needed at beginning of method
def foo
  if condition
    bar
  else
    baz
  end

  qux
end

# good - no blank line needed at end of method
def foo
  bar

  if condition
    baz
  else
    qux
  end
end

Constant Summary collapse

MSG_BEFORE =
"Add empty line before multiline `%<keyword>s` block."
MSG_AFTER =
"Add empty line after multiline `%<keyword>s` block."

Instance Method Summary collapse

Instance Method Details

#assignment_node?(node) ⇒ Boolean

Returns:

  • (Boolean)


148
149
150
# File 'lib/rubocop/cop/sane/empty_lines_around_multiline_block.rb', line 148

def assignment_node?(node)
  [:lvasgn, :ivasgn, :cvasgn, :gvasgn, :casgn, :masgn, :op_asgn, :or_asgn, :and_asgn].include?(node.type)
end

#chained_block?(node) ⇒ Boolean

Returns:

  • (Boolean)


115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/rubocop/cop/sane/empty_lines_around_multiline_block.rb', line 115

def chained_block?(node)
  # Skip blocks that are part of a method chain
  # e.g., expect { ... }.to raise_error
  # e.g., foo.map { ... }&.join (csend is safe navigation &.)
  parent = node.parent
  return true if (parent&.send_type? || parent&.csend_type?) && parent.receiver == node

  # Skip blocks that are part of an assignment expression
  # e.g., self.foo = items.map do ... end
  return true if part_of_assignment?(node)

  false
end

#followed_by_rescue?(node) ⇒ Boolean

Returns:

  • (Boolean)


170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/rubocop/cop/sane/empty_lines_around_multiline_block.rb', line 170

def followed_by_rescue?(node)
  # Don't require blank line before rescue clause
  parent = node.parent
  return false unless parent

  # Check if parent is a rescue node and next sibling is a resbody
  if parent.rescue_type?
    siblings = parent.children
    index = siblings.index(node)
    return false unless index

    next_sib = siblings[index + 1]
    return next_sib&.resbody_type?
  end

  false
end

#lambda_block?(node) ⇒ Boolean

Returns:

  • (Boolean)


111
112
113
# File 'lib/rubocop/cop/sane/empty_lines_around_multiline_block.rb', line 111

def lambda_block?(node)
  node.send_node&.lambda_literal?
end

#on_block(node) ⇒ Object Also known as: on_numblock



100
101
102
103
104
105
106
107
# File 'lib/rubocop/cop/sane/empty_lines_around_multiline_block.rb', line 100

def on_block(node)
  return unless multiline?(node)
  return if chained_block?(node) # e.g., expect { ... }.to raise_error
  return if lambda_block?(node) # e.g., -> { ... } or -> do ... end

  check_empty_line_before(node)
  check_empty_line_after(node)
end

#on_case(node) ⇒ Object Also known as: on_case_match



91
92
93
94
95
96
# File 'lib/rubocop/cop/sane/empty_lines_around_multiline_block.rb', line 91

def on_case(node)
  return unless multiline?(node)

  check_empty_line_before(node)
  check_empty_line_after(node)
end

#on_if(node) ⇒ Object



58
59
60
61
62
63
64
65
66
67
# File 'lib/rubocop/cop/sane/empty_lines_around_multiline_block.rb', line 58

def on_if(node)
  return if node.ternary?
  return if node.modifier_form?
  return if node.elsif? # elsif is part of parent if, not a separate block
  return unless multiline?(node)
  return if part_of_expression?(node)

  check_empty_line_before(node)
  check_empty_line_after(node)
end

#paired_methods?(prev_method, current_method) ⇒ Boolean

Returns:

  • (Boolean)


165
166
167
168
# File 'lib/rubocop/cop/sane/empty_lines_around_multiline_block.rb', line 165

def paired_methods?(prev_method, current_method)
  # desc + task is idiomatic in rake files
  prev_method == :desc && current_method == :task
end

#part_of_assignment?(node) ⇒ Boolean

Returns:

  • (Boolean)


129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/rubocop/cop/sane/empty_lines_around_multiline_block.rb', line 129

def part_of_assignment?(node)
  parent = node.parent
  return false unless parent

  # Direct assignment: foo = bar.map do...end
  return true if assignment_node?(parent)

  # Assignment via setter: self.foo = bar.map do...end
  return true if parent.send_type? && parent.method_name.to_s.end_with?("=")

  # Hash value: { key: items.map do...end }
  return true if parent.pair_type?

  # Array element: [items.map do...end]
  return true if parent.array_type?

  false
end

#part_of_expression?(node) ⇒ Boolean

Returns:

  • (Boolean)


69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/rubocop/cop/sane/empty_lines_around_multiline_block.rb', line 69

def part_of_expression?(node)
  parent = node.parent
  return false unless parent

  # Part of assignment: foo = if ... end
  return true if assignment_node?(parent)

  # Part of setter call: obj.foo = if ... end
  return true if parent.send_type? && parent.method_name.to_s.end_with?("=")

  # Part of method arguments: foo(if ... end)
  return true if parent.send_type? && parent.arguments.include?(node)

  # Part of array: [if ... end]
  return true if parent.array_type?

  # Part of hash value: { key: if ... end }
  return true if parent.pair_type?

  false
end

#preceded_by_paired_method?(node, prev_sibling) ⇒ Boolean

Returns:

  • (Boolean)


152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/rubocop/cop/sane/empty_lines_around_multiline_block.rb', line 152

def preceded_by_paired_method?(node, prev_sibling)
  # Skip blank line requirement for idiomatic pairings like desc + task
  # Only applies to block nodes
  return false unless node.block_type? || node.numblock_type?
  return false unless prev_sibling&.send_type?
  return false unless node.send_node

  prev_method = prev_sibling.method_name
  current_method = node.send_node.method_name

  paired_methods?(prev_method, current_method)
end