+
+ /// Map a Destination to an equivalent expression node
+ ///
+ /// The destination field of a Break or Continue expression can target either an
+ /// expression or a block. The drop range analysis, however, only deals in
+ /// expression nodes, so blocks that might be the destination of a Break or Continue
+ /// will not have a PostOrderId.
+ ///
+ /// If the destination is an expression, this function will simply return that expression's
+ /// hir_id. If the destination is a block, this function will return the hir_id of last
+ /// expression in the block.
+ fn find_target_expression_from_destination(
+ &self,
+ destination: hir::Destination,
+ ) -> Result<HirId, LoopIdError> {
+ destination.target_id.map(|target| {
+ let node = self.hir.get(target);
+ match node {
+ hir::Node::Expr(_) => target,
+ hir::Node::Block(b) => find_last_block_expression(b),
+ hir::Node::Param(..)
+ | hir::Node::Item(..)
+ | hir::Node::ForeignItem(..)
+ | hir::Node::TraitItem(..)
+ | hir::Node::ImplItem(..)
+ | hir::Node::Variant(..)
+ | hir::Node::Field(..)
+ | hir::Node::AnonConst(..)
+ | hir::Node::Stmt(..)
+ | hir::Node::PathSegment(..)
+ | hir::Node::Ty(..)
+ | hir::Node::TraitRef(..)
+ | hir::Node::Binding(..)
+ | hir::Node::Pat(..)
+ | hir::Node::Arm(..)
+ | hir::Node::Local(..)
+ | hir::Node::Ctor(..)
+ | hir::Node::Lifetime(..)
+ | hir::Node::GenericParam(..)
+ | hir::Node::Visibility(..)
+ | hir::Node::Crate(..)
+ | hir::Node::Infer(..) => bug!("Unsupported branch target: {:?}", node),
+ }
+ })
+ }
+}
+
+fn find_last_block_expression(block: &hir::Block<'_>) -> HirId {
+ block.expr.map_or_else(
+ // If there is no tail expression, there will be at least one statement in the
+ // block because the block contains a break or continue statement.
+ || block.stmts.last().unwrap().hir_id,
+ |expr| expr.hir_id,
+ )