]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs
Rollup merge of #102951 - SparrowLii:type_annotation, r=estebank
[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_span::symbol::sym;
9
10 use super::{utils, REDUNDANT_ALLOCATION};
11
12 pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
13     let mut applicability = Applicability::MaybeIncorrect;
14     let outer_sym = if Some(def_id) == cx.tcx.lang_items().owned_box() {
15         "Box"
16     } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) {
17         "Rc"
18     } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) {
19         "Arc"
20     } else {
21         return false;
22     };
23
24     if let Some(span) = utils::match_borrows_parameter(cx, qpath) {
25         let generic_snippet = snippet_with_applicability(cx, span, "..", &mut applicability);
26         span_lint_and_then(
27             cx,
28             REDUNDANT_ALLOCATION,
29             hir_ty.span,
30             &format!("usage of `{outer_sym}<{generic_snippet}>`"),
31             |diag| {
32                 diag.span_suggestion(hir_ty.span, "try", format!("{generic_snippet}"), applicability);
33                 diag.note(&format!(
34                     "`{generic_snippet}` is already a pointer, `{outer_sym}<{generic_snippet}>` allocates a pointer on the heap"
35                 ));
36             },
37         );
38         return true;
39     }
40
41     let Some(ty) = qpath_generic_tys(qpath).next() else { return false };
42     let Some(id) = path_def_id(cx, ty) else { return false };
43     let (inner_sym, ty) = match cx.tcx.get_diagnostic_name(id) {
44         Some(sym::Arc) => ("Arc", ty),
45         Some(sym::Rc) => ("Rc", ty),
46         _ if Some(id) == cx.tcx.lang_items().owned_box() => ("Box", ty),
47         _ => return false,
48     };
49
50     let TyKind::Path(inner_qpath) = &ty.kind else {
51         return false
52     };
53     let inner_span = match qpath_generic_tys(inner_qpath).next() {
54         Some(ty) => {
55             // Reallocation of a fat pointer causes it to become thin. `hir_ty_to_ty` is safe to use
56             // here because `mod.rs` guarantees this lint is only run on types outside of bodies and
57             // is not run on locals.
58             if !hir_ty_to_ty(cx.tcx, ty).is_sized(cx.tcx.at(ty.span), cx.param_env) {
59                 return false;
60             }
61             ty.span
62         },
63         None => return false,
64     };
65     if inner_sym == outer_sym {
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_sym}<{generic_snippet}>` is already on the heap, `{outer_sym}<{inner_sym}<{generic_snippet}>>` makes an extra allocation"
81                 ));
82             },
83         );
84     } else {
85         let generic_snippet = snippet(cx, inner_span, "..");
86         span_lint_and_then(
87             cx,
88             REDUNDANT_ALLOCATION,
89             hir_ty.span,
90             &format!("usage of `{outer_sym}<{inner_sym}<{generic_snippet}>>`"),
91             |diag| {
92                 diag.note(&format!(
93                     "`{inner_sym}<{generic_snippet}>` is already on the heap, `{outer_sym}<{inner_sym}<{generic_snippet}>>` makes an extra allocation"
94                 ));
95                 diag.help(&format!(
96                     "consider using just `{outer_sym}<{generic_snippet}>` or `{inner_sym}<{generic_snippet}>`"
97                 ));
98             },
99         );
100     }
101     true
102 }