class RuboCop::Cop::Lint::UnreachableLoop

Checks for loops that will have at most one iteration.

A loop that can never reach the second iteration is a possible error in the code. In rare cases where only one iteration (or at most one iteration) is intended behavior, the code should be refactored to use ‘if` conditionals.

NOTE: Block methods that are used with “Enumerable“s are considered to be loops.

‘AllowedPatterns` can be used to match against the block receiver in order to allow code that would otherwise be registered as an offense (eg. `times` used not in an `Enumerable` context).

@example

# bad
while node
  do_something(node)
  node = node.parent
  break
end

# good
while node
  do_something(node)
  node = node.parent
end

# bad
def verify_list(head)
  item = head
  begin
    if verify(item)
      return true
    else
      return false
    end
  end while(item)
end

# good
def verify_list(head)
  item = head
  begin
    if verify(item)
      item = item.next
    else
      return false
    end
  end while(item)

  true
end

# bad
def find_something(items)
  items.each do |item|
    if something?(item)
      return item
    else
      raise NotFoundError
    end
  end
end

# good
def find_something(items)
  items.each do |item|
    if something?(item)
      return item
    end
  end
  raise NotFoundError
end

# bad
2.times { raise ArgumentError }

@example AllowedPatterns: [‘(exactly|at_least|at_most)(d+).times’] (default)

# good
exactly(2).times { raise StandardError }

Constants

CONTINUE_KEYWORDS
MSG

Public Instance Methods

on_block(node) click to toggle source
# File lib/rubocop/cop/lint/unreachable_loop.rb, line 100
def on_block(node)
  check(node) if loop_method?(node)
end
on_for(node)
Alias for: on_while
on_numblock(node) click to toggle source
# File lib/rubocop/cop/lint/unreachable_loop.rb, line 104
def on_numblock(node)
  check(node) if loop_method?(node)
end
on_until(node)
Alias for: on_while
on_until_post(node)
Alias for: on_while
on_while(node) click to toggle source
# File lib/rubocop/cop/lint/unreachable_loop.rb, line 92
def on_while(node)
  check(node)
end
on_while_post(node)
Alias for: on_while

Private Instance Methods

break_statement?(node) click to toggle source
# File lib/rubocop/cop/lint/unreachable_loop.rb, line 153
def break_statement?(node)
  return true if break_command?(node)

  case node.type
  when :begin, :kwbegin
    statements = *node
    break_statement = statements.find { |statement| break_statement?(statement) }
    break_statement && !preceded_by_continue_statement?(break_statement)
  when :if
    check_if(node)
  when :case, :case_match
    check_case(node)
  else
    false
  end
end
check(node) click to toggle source
# File lib/rubocop/cop/lint/unreachable_loop.rb, line 119
def check(node)
  statements = statements(node)
  break_statement = statements.find { |statement| break_statement?(statement) }
  return unless break_statement

  unless preceded_by_continue_statement?(break_statement) ||
         conditional_continue_keyword?(break_statement)
    add_offense(node)
  end
end
check_case(node) click to toggle source
# File lib/rubocop/cop/lint/unreachable_loop.rb, line 176
def check_case(node)
  else_branch = node.else_branch
  return false unless else_branch
  return false unless break_statement?(else_branch)

  branches = if node.case_type?
               node.when_branches
             else
               node.in_pattern_branches
             end

  branches.all? { |branch| branch.body && break_statement?(branch.body) }
end
check_if(node) click to toggle source
# File lib/rubocop/cop/lint/unreachable_loop.rb, line 170
def check_if(node)
  if_branch = node.if_branch
  else_branch = node.else_branch
  if_branch && else_branch && break_statement?(if_branch) && break_statement?(else_branch)
end
conditional_continue_keyword?(break_statement) click to toggle source
# File lib/rubocop/cop/lint/unreachable_loop.rb, line 200
def conditional_continue_keyword?(break_statement)
  or_node = break_statement.each_descendant(:or).to_a.last

  or_node && CONTINUE_KEYWORDS.include?(or_node.rhs.type)
end
loop_method?(node) click to toggle source
# File lib/rubocop/cop/lint/unreachable_loop.rb, line 110
def loop_method?(node)
  return false unless node.block_type? || node.numblock_type?

  send_node = node.send_node
  loopable = send_node.enumerable_method? || send_node.enumerator_method? ||
             send_node.method?(:loop)
  loopable && !matches_allowed_pattern?(send_node.source)
end
preceded_by_continue_statement?(break_statement) click to toggle source
# File lib/rubocop/cop/lint/unreachable_loop.rb, line 190
def preceded_by_continue_statement?(break_statement)
  break_statement.left_siblings.any? do |sibling|
    # Numblocks have the arguments count as a number in the AST.
    next if sibling.is_a?(Integer)
    next if sibling.loop_keyword? || loop_method?(sibling)

    sibling.each_descendant(*CONTINUE_KEYWORDS).any?
  end
end
statements(node) click to toggle source
# File lib/rubocop/cop/lint/unreachable_loop.rb, line 130
def statements(node)
  body = node.body

  if body.nil?
    []
  elsif body.begin_type?
    body.children
  else
    [body]
  end
end