]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
New upstream version 1.53.0+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / types / borrowed_box.rs
CommitLineData
cdc7bbd5
XL
1use clippy_utils::diagnostics::span_lint_and_sugg;
2use clippy_utils::source::snippet;
3use clippy_utils::{match_def_path, paths};
4use if_chain::if_chain;
f20569fa
XL
5use rustc_errors::Applicability;
6use rustc_hir::{
7 self as hir, GenericArg, GenericBounds, GenericParamKind, HirId, Lifetime, MutTy, Mutability, Node, QPath,
8 SyntheticTyParamKind, TyKind,
9};
10use rustc_lint::LateContext;
11
f20569fa
XL
12use super::BORROWED_BOX;
13
14pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, mut_ty: &MutTy<'_>) -> bool {
15 match mut_ty.ty.kind {
16 TyKind::Path(ref qpath) => {
17 let hir_id = mut_ty.ty.hir_id;
18 let def = cx.qpath_res(qpath, hir_id);
19 if_chain! {
20 if let Some(def_id) = def.opt_def_id();
21 if Some(def_id) == cx.tcx.lang_items().owned_box();
cdc7bbd5 22 if let QPath::Resolved(None, path) = *qpath;
f20569fa 23 if let [ref bx] = *path.segments;
cdc7bbd5 24 if let Some(params) = bx.args;
f20569fa
XL
25 if !params.parenthesized;
26 if let Some(inner) = params.args.iter().find_map(|arg| match arg {
27 GenericArg::Type(ty) => Some(ty),
28 _ => None,
29 });
30 then {
cdc7bbd5 31 if is_any_trait(cx, inner) {
f20569fa
XL
32 // Ignore `Box<Any>` types; see issue #1884 for details.
33 return false;
34 }
35
36 let ltopt = if lt.is_elided() {
37 String::new()
38 } else {
39 format!("{} ", lt.name.ident().as_str())
40 };
41
42 if mut_ty.mutbl == Mutability::Mut {
43 // Ignore `&mut Box<T>` types; see issue #2907 for
44 // details.
45 return false;
46 }
47
48 // When trait objects or opaque types have lifetime or auto-trait bounds,
49 // we need to add parentheses to avoid a syntax error due to its ambiguity.
50 // Originally reported as the issue #3128.
51 let inner_snippet = snippet(cx, inner.span, "..");
52 let suggestion = match &inner.kind {
53 TyKind::TraitObject(bounds, lt_bound, _) if bounds.len() > 1 || !lt_bound.is_elided() => {
54 format!("&{}({})", ltopt, &inner_snippet)
55 },
56 TyKind::Path(qpath)
57 if get_bounds_if_impl_trait(cx, qpath, inner.hir_id)
58 .map_or(false, |bounds| bounds.len() > 1) =>
59 {
60 format!("&{}({})", ltopt, &inner_snippet)
61 },
62 _ => format!("&{}{}", ltopt, &inner_snippet),
63 };
64 span_lint_and_sugg(
65 cx,
66 BORROWED_BOX,
67 hir_ty.span,
68 "you seem to be trying to use `&Box<T>`. Consider using just `&T`",
69 "try",
70 suggestion,
71 // To make this `MachineApplicable`, at least one needs to check if it isn't a trait item
72 // because the trait impls of it will break otherwise;
73 // and there may be other cases that result in invalid code.
74 // For example, type coercion doesn't work nicely.
75 Applicability::Unspecified,
76 );
77 return true;
78 }
79 };
80 false
81 },
82 _ => false,
83 }
84}
85
86// Returns true if given type is `Any` trait.
cdc7bbd5 87fn is_any_trait(cx: &LateContext<'_>, t: &hir::Ty<'_>) -> bool {
f20569fa 88 if_chain! {
cdc7bbd5 89 if let TyKind::TraitObject(traits, ..) = t.kind;
f20569fa 90 if !traits.is_empty();
cdc7bbd5 91 if let Some(trait_did) = traits[0].trait_ref.trait_def_id();
f20569fa
XL
92 // Only Send/Sync can be used as additional traits, so it is enough to
93 // check only the first trait.
cdc7bbd5 94 if match_def_path(cx, trait_did, &paths::ANY_TRAIT);
f20569fa
XL
95 then {
96 return true;
97 }
98 }
99
100 false
101}
102
103fn get_bounds_if_impl_trait<'tcx>(cx: &LateContext<'tcx>, qpath: &QPath<'_>, id: HirId) -> Option<GenericBounds<'tcx>> {
104 if_chain! {
105 if let Some(did) = cx.qpath_res(qpath, id).opt_def_id();
106 if let Some(Node::GenericParam(generic_param)) = cx.tcx.hir().get_if_local(did);
107 if let GenericParamKind::Type { synthetic, .. } = generic_param.kind;
108 if synthetic == Some(SyntheticTyParamKind::ImplTrait);
109 then {
110 Some(generic_param.bounds)
111 } else {
112 None
113 }
114 }
115}