]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs
Rollup merge of #106260 - chenyukang:yukang/fix-106213-doc, r=GuillaumeGomez
[rust.git] / src / tools / clippy / 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_id::DefId, QPath, TyKind};
6 use rustc_hir_analysis::hir_ty_to_ty;
7 use rustc_lint::LateContext;
8 use rustc_middle::ty::TypeVisitable;
9 use rustc_span::symbol::sym;
10
11 use super::{utils, REDUNDANT_ALLOCATION};
12
13 pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
14     let mut applicability = Applicability::MaybeIncorrect;
15     let outer_sym = if Some(def_id) == cx.tcx.lang_items().owned_box() {
16         "Box"
17     } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) {
18         "Rc"
19     } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) {
20         "Arc"
21     } else {
22         return false;
23     };
24
25     if let Some(span) = utils::match_borrows_parameter(cx, qpath) {
26         let generic_snippet = snippet_with_applicability(cx, span, "..", &mut applicability);
27         span_lint_and_then(
28             cx,
29             REDUNDANT_ALLOCATION,
30             hir_ty.span,
31             &format!("usage of `{outer_sym}<{generic_snippet}>`"),
32             |diag| {
33                 diag.span_suggestion(hir_ty.span, "try", format!("{generic_snippet}"), applicability);
34                 diag.note(format!(
35                     "`{generic_snippet}` is already a pointer, `{outer_sym}<{generic_snippet}>` allocates a pointer on the heap"
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 TyKind::Path(inner_qpath) = &ty.kind else {
52         return false
53     };
54     let inner_span = match qpath_generic_tys(inner_qpath).next() {
55         Some(hir_ty) => {
56             // Reallocation of a fat pointer causes it to become thin. `hir_ty_to_ty` is safe to use
57             // here because `mod.rs` guarantees this lint is only run on types outside of bodies and
58             // is not run on locals.
59             let ty = hir_ty_to_ty(cx.tcx, hir_ty);
60             if ty.has_escaping_bound_vars() || !ty.is_sized(cx.tcx, cx.param_env) {
61                 return false;
62             }
63             hir_ty.span
64         },
65         None => return false,
66     };
67     if inner_sym == outer_sym {
68         let generic_snippet = snippet_with_applicability(cx, inner_span, "..", &mut applicability);
69         span_lint_and_then(
70             cx,
71             REDUNDANT_ALLOCATION,
72             hir_ty.span,
73             &format!("usage of `{outer_sym}<{inner_sym}<{generic_snippet}>>`"),
74             |diag| {
75                 diag.span_suggestion(
76                     hir_ty.span,
77                     "try",
78                     format!("{outer_sym}<{generic_snippet}>"),
79                     applicability,
80                 );
81                 diag.note(format!(
82                     "`{inner_sym}<{generic_snippet}>` is already on the heap, `{outer_sym}<{inner_sym}<{generic_snippet}>>` makes an extra allocation"
83                 ));
84             },
85         );
86     } else {
87         let generic_snippet = snippet(cx, inner_span, "..");
88         span_lint_and_then(
89             cx,
90             REDUNDANT_ALLOCATION,
91             hir_ty.span,
92             &format!("usage of `{outer_sym}<{inner_sym}<{generic_snippet}>>`"),
93             |diag| {
94                 diag.note(format!(
95                     "`{inner_sym}<{generic_snippet}>` is already on the heap, `{outer_sym}<{inner_sym}<{generic_snippet}>>` makes an extra allocation"
96                 ));
97                 diag.help(format!(
98                     "consider using just `{outer_sym}<{generic_snippet}>` or `{inner_sym}<{generic_snippet}>`"
99                 ));
100             },
101         );
102     }
103     true
104 }