+ if let Some(higher::IfLet {
+ if_else,
+ let_pat,
+ let_expr,
+ ..
+ }) = higher::IfLet::ast(cx, expr)
+ {
+ find_sugg_for_if_let(cx, expr, let_pat, let_expr, "if", if_else.is_some())
+ }
+ if let ExprKind::Match(op, arms, MatchSource::Normal) = &expr.kind {
+ find_sugg_for_match(cx, expr, op, arms)
+ }
+ if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) {
+ find_sugg_for_if_let(cx, expr, let_pat, let_expr, "while", false)
+ }
+ }
+
+ /// Checks if the drop order for a type matters. Some std types implement drop solely to
+ /// deallocate memory. For these types, and composites containing them, changing the drop order
+ /// won't result in any observable side effects.
+ fn type_needs_ordered_drop(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+ type_needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
+ }
+
+ fn type_needs_ordered_drop_inner(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool {
+ if !seen.insert(ty) {
+ return false;
+ }
+ if !ty.needs_drop(cx.tcx, cx.param_env) {
+ false
+ } else if !cx
+ .tcx
+ .lang_items()
+ .drop_trait()
+ .map_or(false, |id| implements_trait(cx, ty, id, &[]))
+ {
+ // This type doesn't implement drop, so no side effects here.
+ // Check if any component type has any.
+ match ty.kind() {
+ ty::Tuple(_) => ty.tuple_fields().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)),
+ ty::Array(ty, _) => type_needs_ordered_drop_inner(cx, ty, seen),
+ ty::Adt(adt, subs) => adt
+ .all_fields()
+ .map(|f| f.ty(cx.tcx, subs))
+ .any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)),
+ _ => true,
+ }
+ }
+ // Check for std types which implement drop, but only for memory allocation.
+ else if is_type_diagnostic_item(cx, ty, sym::vec_type)
+ || is_type_lang_item(cx, ty, LangItem::OwnedBox)
+ || is_type_diagnostic_item(cx, ty, sym::Rc)
+ || is_type_diagnostic_item(cx, ty, sym::Arc)
+ || is_type_diagnostic_item(cx, ty, sym::cstring_type)
+ || is_type_diagnostic_item(cx, ty, sym::BTreeMap)
+ || is_type_diagnostic_item(cx, ty, sym::LinkedList)
+ || match_type(cx, ty, &paths::WEAK_RC)
+ || match_type(cx, ty, &paths::WEAK_ARC)
+ {
+ // Check all of the generic arguments.
+ if let ty::Adt(_, subs) = ty.kind() {
+ subs.types().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen))
+ } else {
+ true
+ }
+ } else {
+ true
+ }
+ }
+
+ // Extract the generic arguments out of a type
+ fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option<Ty<'_>> {
+ if_chain! {
+ if let ty::Adt(_, subs) = ty.kind();
+ if let Some(sub) = subs.get(index);
+ if let GenericArgKind::Type(sub_ty) = sub.unpack();
+ then {
+ Some(sub_ty)
+ } else {
+ None
+ }
+ }
+ }
+
+ // Checks if there are any temporaries created in the given expression for which drop order
+ // matters.
+ fn temporaries_need_ordered_drop(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+ struct V<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ res: bool,
+ }
+ impl<'a, 'tcx> Visitor<'tcx> for V<'a, 'tcx> {
+ type Map = ErasedMap<'tcx>;
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
+ }
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+ match expr.kind {
+ // Taking the reference of a value leaves a temporary
+ // e.g. In `&String::new()` the string is a temporary value.
+ // Remaining fields are temporary values
+ // e.g. In `(String::new(), 0).1` the string is a temporary value.
+ ExprKind::AddrOf(_, _, expr) | ExprKind::Field(expr, _) => {
+ if !matches!(expr.kind, ExprKind::Path(_)) {
+ if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) {
+ self.res = true;
+ } else {
+ self.visit_expr(expr);
+ }
+ }
+ },
+ // the base type is alway taken by reference.
+ // e.g. In `(vec![0])[0]` the vector is a temporary value.
+ ExprKind::Index(base, index) => {
+ if !matches!(base.kind, ExprKind::Path(_)) {
+ if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) {
+ self.res = true;
+ } else {
+ self.visit_expr(base);
+ }
+ }
+ self.visit_expr(index);
+ },
+ // Method calls can take self by reference.
+ // e.g. In `String::new().len()` the string is a temporary value.
+ ExprKind::MethodCall(_, _, [self_arg, args @ ..], _) => {
+ if !matches!(self_arg.kind, ExprKind::Path(_)) {
+ let self_by_ref = self
+ .cx
+ .typeck_results()
+ .type_dependent_def_id(expr.hir_id)
+ .map_or(false, |id| self.cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref());
+ if self_by_ref
+ && type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg))
+ {
+ self.res = true;
+ } else {
+ self.visit_expr(self_arg);
+ }
+ }
+ args.iter().for_each(|arg| self.visit_expr(arg));
+ },
+ // Either explicitly drops values, or changes control flow.
+ ExprKind::DropTemps(_)
+ | ExprKind::Ret(_)
+ | ExprKind::Break(..)
+ | ExprKind::Yield(..)
+ | ExprKind::Block(Block { expr: None, .. }, _)
+ | ExprKind::Loop(..) => (),
+
+ // Only consider the final expression.
+ ExprKind::Block(Block { expr: Some(expr), .. }, _) => self.visit_expr(expr),
+
+ _ => walk_expr(self, expr),
+ }