]>
Commit | Line | Data |
---|---|---|
cdc7bbd5 XL |
1 | use clippy_utils::diagnostics::span_lint_and_sugg; |
2 | use clippy_utils::source::snippet; | |
3 | use clippy_utils::{match_def_path, paths}; | |
4 | use if_chain::if_chain; | |
f20569fa XL |
5 | use rustc_errors::Applicability; |
6 | use rustc_hir::{ | |
7 | self as hir, GenericArg, GenericBounds, GenericParamKind, HirId, Lifetime, MutTy, Mutability, Node, QPath, | |
8 | SyntheticTyParamKind, TyKind, | |
9 | }; | |
10 | use rustc_lint::LateContext; | |
11 | ||
f20569fa XL |
12 | use super::BORROWED_BOX; |
13 | ||
14 | pub(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 | 87 | fn 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 | ||
103 | fn 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 | } |