use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::last_path_segment; use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{self as hir, def_id::DefId, GenericArg, QPath, TyKind}; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::LateContext; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::TypeVisitableExt; use rustc_span::symbol::sym; use super::VEC_BOX; pub(super) fn check( cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId, box_size_threshold: u64, ) -> bool { if cx.tcx.is_diagnostic_item(sym::Vec, def_id) { if_chain! { // Get the _ part of Vec<_> if let Some(last) = last_path_segment(qpath).args; if let Some(ty) = last.args.iter().find_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), _ => None, }); // ty is now _ at this point if let TyKind::Path(ref ty_qpath) = ty.kind; let res = cx.qpath_res(ty_qpath, ty.hir_id); if let Some(def_id) = res.opt_def_id(); if Some(def_id) == cx.tcx.lang_items().owned_box(); // At this point, we know ty is Box, now get T if let Some(last) = last_path_segment(ty_qpath).args; if let Some(boxed_ty) = last.args.iter().find_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), _ => None, }); let ty_ty = hir_ty_to_ty(cx.tcx, boxed_ty); if !ty_ty.has_escaping_bound_vars(); if ty_ty.is_sized(cx.tcx, cx.param_env); if let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes()); if ty_ty_size < box_size_threshold; then { span_lint_and_sugg( cx, VEC_BOX, hir_ty.span, "`Vec` is already on the heap, the boxing is unnecessary", "try", format!("Vec<{}>", snippet(cx, boxed_ty.span, "..")), Applicability::MachineApplicable, ); true } else { false } } } else { false } }