]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/types/redundant_allocation.rs
Fix redundant_allocation warning for Rc<Box<str>>
[rust.git] / clippy_lints / src / types / redundant_allocation.rs
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;
8
9 use super::{utils, REDUNDANT_ALLOCATION};
10
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() {
13         "Box"
14     } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) {
15         "Rc"
16     } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) {
17         "Arc"
18     } else {
19         return false;
20     };
21
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);
25         span_lint_and_then(
26             cx,
27             REDUNDANT_ALLOCATION,
28             hir_ty.span,
29             &format!("usage of `{}<{}>`", outer_sym, generic_snippet),
30             |diag| {
31                 diag.span_suggestion(hir_ty.span, "try", format!("{}", generic_snippet), applicability);
32                 diag.note(&format!(
33                     "`{generic}` is already a pointer, `{outer}<{generic}>` allocates a pointer on the heap",
34                     outer = outer_sym,
35                     generic = generic_snippet
36                 ));
37             },
38         );
39         return true;
40     }
41
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),
48         _ => return false,
49     };
50
51     let inner_qpath = match &ty.kind {
52         TyKind::Path(inner_qpath) => inner_qpath,
53         _ => return false,
54     };
55     let inner_span = match qpath_generic_tys(inner_qpath).next() {
56         Some(ty) => {
57             if alloc_makes_pointer_thin(cx, ty) {
58                 return false;
59             }
60             ty.span
61         },
62         None => return false,
63     };
64     if inner_sym == outer_sym {
65         let mut applicability = Applicability::MaybeIncorrect;
66         let generic_snippet = snippet_with_applicability(cx, inner_span, "..", &mut applicability);
67         span_lint_and_then(
68             cx,
69             REDUNDANT_ALLOCATION,
70             hir_ty.span,
71             &format!("usage of `{}<{}<{}>>`", outer_sym, inner_sym, generic_snippet),
72             |diag| {
73                 diag.span_suggestion(
74                     hir_ty.span,
75                     "try",
76                     format!("{}<{}>", outer_sym, generic_snippet),
77                     applicability,
78                 );
79                 diag.note(&format!(
80                     "`{inner}<{generic}>` is already on the heap, `{outer}<{inner}<{generic}>>` makes an extra allocation",
81                     outer = outer_sym,
82                     inner = inner_sym,
83                     generic = generic_snippet
84                 ));
85             },
86         );
87     } else {
88         let generic_snippet = snippet(cx, inner_span, "..");
89         span_lint_and_then(
90             cx,
91             REDUNDANT_ALLOCATION,
92             hir_ty.span,
93             &format!("usage of `{}<{}<{}>>`", outer_sym, inner_sym, generic_snippet),
94             |diag| {
95                 diag.note(&format!(
96                     "`{inner}<{generic}>` is already on the heap, `{outer}<{inner}<{generic}>>` makes an extra allocation",
97                     outer = outer_sym,
98                     inner = inner_sym,
99                     generic = generic_snippet
100                 ));
101                 diag.help(&format!(
102                     "consider using just `{outer}<{generic}>` or `{inner}<{generic}>`",
103                     outer = outer_sym,
104                     inner = inner_sym,
105                     generic = generic_snippet
106                 ));
107             },
108         );
109     }
110     true
111 }
112
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 {
115     match &hir_ty.kind {
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) {
121                     return true;
122                 }
123             }
124             false
125         },
126         _ => false,
127     }
128 }