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