1 use clippy_utils::diagnostics::span_lint_and_then;
2 use clippy_utils::source::{snippet, snippet_with_applicability};
3 use clippy_utils::{path_def_id, qpath_generic_tys};
4 use rustc_errors::Applicability;
5 use rustc_hir::{self as hir, def, def_id::DefId, PrimTy, QPath, TyKind};
6 use rustc_lint::LateContext;
7 use rustc_span::symbol::sym;
9 use super::{utils, REDUNDANT_ALLOCATION};
11 pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
12 let outer_sym = if Some(def_id) == cx.tcx.lang_items().owned_box() {
14 } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) {
16 } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) {
22 if let Some(span) = utils::match_borrows_parameter(cx, qpath) {
23 let mut applicability = Applicability::MaybeIncorrect;
24 let generic_snippet = snippet_with_applicability(cx, span, "..", &mut applicability);
29 &format!("usage of `{}<{}>`", outer_sym, generic_snippet),
31 diag.span_suggestion(hir_ty.span, "try", format!("{}", generic_snippet), applicability);
33 "`{generic}` is already a pointer, `{outer}<{generic}>` allocates a pointer on the heap",
35 generic = generic_snippet
42 let Some(ty) = qpath_generic_tys(qpath).next() else { return false };
43 let Some(id) = path_def_id(cx, ty) else { return false };
44 let (inner_sym, ty) = match cx.tcx.get_diagnostic_name(id) {
45 Some(sym::Arc) => ("Arc", ty),
46 Some(sym::Rc) => ("Rc", ty),
47 _ if Some(id) == cx.tcx.lang_items().owned_box() => ("Box", ty),
51 let inner_qpath = match &ty.kind {
52 TyKind::Path(inner_qpath) => inner_qpath,
55 let inner_span = match qpath_generic_tys(inner_qpath).next() {
57 if alloc_makes_pointer_thin(cx, ty) {
64 if inner_sym == outer_sym {
65 let mut applicability = Applicability::MaybeIncorrect;
66 let generic_snippet = snippet_with_applicability(cx, inner_span, "..", &mut applicability);
71 &format!("usage of `{}<{}<{}>>`", outer_sym, inner_sym, generic_snippet),
76 format!("{}<{}>", outer_sym, generic_snippet),
80 "`{inner}<{generic}>` is already on the heap, `{outer}<{inner}<{generic}>>` makes an extra allocation",
83 generic = generic_snippet
88 let generic_snippet = snippet(cx, inner_span, "..");
93 &format!("usage of `{}<{}<{}>>`", outer_sym, inner_sym, generic_snippet),
96 "`{inner}<{generic}>` is already on the heap, `{outer}<{inner}<{generic}>>` makes an extra allocation",
99 generic = generic_snippet
102 "consider using just `{outer}<{generic}>` or `{inner}<{generic}>`",
105 generic = generic_snippet
113 /// Returns `true` if the allocation would make `hir_ty` go from fat to thin.
114 fn alloc_makes_pointer_thin(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) -> bool {
116 TyKind::TraitObject(..) => true,
117 TyKind::Path(ty_qpath) => {
118 let ty_res = cx.qpath_res(ty_qpath, hir_ty.hir_id);
119 if let def::Res::PrimTy(prim_ty) = ty_res {
120 if matches!(prim_ty, PrimTy::Str) {