1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::{path_def_id, qpath_generic_tys};
use rustc_errors::Applicability;
use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind};
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
use rustc_typeck::hir_ty_to_ty;
use super::{utils, REDUNDANT_ALLOCATION};
pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
let outer_sym = if Some(def_id) == cx.tcx.lang_items().owned_box() {
"Box"
} else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) {
"Rc"
} else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) {
"Arc"
} else {
return false;
};
if let Some(span) = utils::match_borrows_parameter(cx, qpath) {
let mut applicability = Applicability::MaybeIncorrect;
let generic_snippet = snippet_with_applicability(cx, span, "..", &mut applicability);
span_lint_and_then(
cx,
REDUNDANT_ALLOCATION,
hir_ty.span,
&format!("usage of `{}<{}>`", outer_sym, generic_snippet),
|diag| {
diag.span_suggestion(hir_ty.span, "try", format!("{}", generic_snippet), applicability);
diag.note(&format!(
"`{generic}` is already a pointer, `{outer}<{generic}>` allocates a pointer on the heap",
outer = outer_sym,
generic = generic_snippet
));
},
);
return true;
}
let Some(ty) = qpath_generic_tys(qpath).next() else { return false };
let Some(id) = path_def_id(cx, ty) else { return false };
let (inner_sym, ty) = match cx.tcx.get_diagnostic_name(id) {
Some(sym::Arc) => ("Arc", ty),
Some(sym::Rc) => ("Rc", ty),
_ if Some(id) == cx.tcx.lang_items().owned_box() => ("Box", ty),
_ => return false,
};
let inner_qpath = match &ty.kind {
TyKind::Path(inner_qpath) => inner_qpath,
_ => return false,
};
let inner_span = match qpath_generic_tys(inner_qpath).next() {
Some(ty) => {
// Reallocation of a fat pointer causes it to become thin. `hir_ty_to_ty` is safe to use
// here because `mod.rs` guarantees this lint is only run on types outside of bodies and
// is not run on locals.
if !hir_ty_to_ty(cx.tcx, ty).is_sized(cx.tcx.at(ty.span), cx.param_env) {
return false;
}
ty.span
},
None => return false,
};
if inner_sym == outer_sym {
let mut applicability = Applicability::MaybeIncorrect;
let generic_snippet = snippet_with_applicability(cx, inner_span, "..", &mut applicability);
span_lint_and_then(
cx,
REDUNDANT_ALLOCATION,
hir_ty.span,
&format!("usage of `{}<{}<{}>>`", outer_sym, inner_sym, generic_snippet),
|diag| {
diag.span_suggestion(
hir_ty.span,
"try",
format!("{}<{}>", outer_sym, generic_snippet),
applicability,
);
diag.note(&format!(
"`{inner}<{generic}>` is already on the heap, `{outer}<{inner}<{generic}>>` makes an extra allocation",
outer = outer_sym,
inner = inner_sym,
generic = generic_snippet
));
},
);
} else {
let generic_snippet = snippet(cx, inner_span, "..");
span_lint_and_then(
cx,
REDUNDANT_ALLOCATION,
hir_ty.span,
&format!("usage of `{}<{}<{}>>`", outer_sym, inner_sym, generic_snippet),
|diag| {
diag.note(&format!(
"`{inner}<{generic}>` is already on the heap, `{outer}<{inner}<{generic}>>` makes an extra allocation",
outer = outer_sym,
inner = inner_sym,
generic = generic_snippet
));
diag.help(&format!(
"consider using just `{outer}<{generic}>` or `{inner}<{generic}>`",
outer = outer_sym,
inner = inner_sym,
generic = generic_snippet
));
},
);
}
true
}
|