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_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_while(node)
click to toggle source
# File lib/rubocop/cop/lint/unreachable_loop.rb, line 92 def on_while(node) check(node) end
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