]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs
rustc_typeck to rustc_hir_analysis
[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_lint::LateContext;
7 use rustc_span::symbol::sym;
8 use rustc_hir_analysis::hir_ty_to_ty;
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 outer_sym = if Some(def_id) == cx.tcx.lang_items().owned_box() {
14         "Box"
15     } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) {
16         "Rc"
17     } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) {
18         "Arc"
19     } else {
20         return false;
21     };
22
23     if let Some(span) = utils::match_borrows_parameter(cx, qpath) {
24         let mut applicability = Applicability::MaybeIncorrect;
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}` is already a pointer, `{outer}<{generic}>` allocates a pointer on the heap",
35                     outer = outer_sym,
36                     generic = generic_snippet
37                 ));
38             },
39         );
40         return true;
41     }
42
43     let Some(ty) = qpath_generic_tys(qpath).next() else { return false };
44     let Some(id) = path_def_id(cx, ty) else { return false };
45     let (inner_sym, ty) = match cx.tcx.get_diagnostic_name(id) {
46         Some(sym::Arc) => ("Arc", ty),
47         Some(sym::Rc) => ("Rc", ty),
48         _ if Some(id) == cx.tcx.lang_items().owned_box() => ("Box", ty),
49         _ => return false,
50     };
51
52     let inner_qpath = match &ty.kind {
53         TyKind::Path(inner_qpath) => inner_qpath,
54         _ => return false,
55     };
56     let inner_span = match qpath_generic_tys(inner_qpath).next() {
57         Some(ty) => {
58             // Reallocation of a fat pointer causes it to become thin. `hir_ty_to_ty` is safe to use
59             // here because `mod.rs` guarantees this lint is only run on types outside of bodies and
60             // is not run on locals.
61             if !hir_ty_to_ty(cx.tcx, ty).is_sized(cx.tcx.at(ty.span), cx.param_env) {
62                 return false;
63             }
64             ty.span
65         },
66         None => return false,
67     };
68     if inner_sym == outer_sym {
69         let mut applicability = Applicability::MaybeIncorrect;
70         let generic_snippet = snippet_with_applicability(cx, inner_span, "..", &mut applicability);
71         span_lint_and_then(
72             cx,
73             REDUNDANT_ALLOCATION,
74             hir_ty.span,
75             &format!("usage of `{}<{}<{}>>`", outer_sym, inner_sym, generic_snippet),
76             |diag| {
77                 diag.span_suggestion(
78                     hir_ty.span,
79                     "try",
80                     format!("{}<{}>", outer_sym, generic_snippet),
81                     applicability,
82                 );
83                 diag.note(&format!(
84                     "`{inner}<{generic}>` is already on the heap, `{outer}<{inner}<{generic}>>` makes an extra allocation",
85                     outer = outer_sym,
86                     inner = inner_sym,
87                     generic = generic_snippet
88                 ));
89             },
90         );
91     } else {
92         let generic_snippet = snippet(cx, inner_span, "..");
93         span_lint_and_then(
94             cx,
95             REDUNDANT_ALLOCATION,
96             hir_ty.span,
97             &format!("usage of `{}<{}<{}>>`", outer_sym, inner_sym, generic_snippet),
98             |diag| {
99                 diag.note(&format!(
100                     "`{inner}<{generic}>` is already on the heap, `{outer}<{inner}<{generic}>>` makes an extra allocation",
101                     outer = outer_sym,
102                     inner = inner_sym,
103                     generic = generic_snippet
104                 ));
105                 diag.help(&format!(
106                     "consider using just `{outer}<{generic}>` or `{inner}<{generic}>`",
107                     outer = outer_sym,
108                     inner = inner_sym,
109                     generic = generic_snippet
110                 ));
111             },
112         );
113     }
114     true
115 }