| 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::def_id::DefId; |
| use rustc_hir::{self as hir, 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<T>, 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<T>` is already on the heap, the boxing is unnecessary", |
| "try", |
| format!("Vec<{}>", snippet(cx, boxed_ty.span, "..")), |
| Applicability::MachineApplicable, |
| ); |
| true |
| } else { |
| false |
| } |
| } |
| } else { |
| false |
| } |
| } |