|
| 1 | +use rustc_errors::Applicability; |
| 2 | +use rustc_hir::{ |
| 3 | + self as hir, GenericArg, GenericBounds, GenericParamKind, HirId, Lifetime, MutTy, Mutability, Node, QPath, |
| 4 | + SyntheticTyParamKind, TyKind, |
| 5 | +}; |
| 6 | +use rustc_lint::LateContext; |
| 7 | + |
| 8 | +use if_chain::if_chain; |
| 9 | + |
| 10 | +use crate::utils::{match_path, paths, snippet, span_lint_and_sugg}; |
| 11 | + |
| 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(); |
| 22 | + if let QPath::Resolved(None, ref path) = *qpath; |
| 23 | + if let [ref bx] = *path.segments; |
| 24 | + if let Some(ref params) = bx.args; |
| 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 { |
| 31 | + if is_any_trait(inner) { |
| 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. |
| 87 | +fn is_any_trait(t: &hir::Ty<'_>) -> bool { |
| 88 | + if_chain! { |
| 89 | + if let TyKind::TraitObject(ref traits, _) = t.kind; |
| 90 | + if !traits.is_empty(); |
| 91 | + // Only Send/Sync can be used as additional traits, so it is enough to |
| 92 | + // check only the first trait. |
| 93 | + if match_path(&traits[0].trait_ref.path, &paths::ANY_TRAIT); |
| 94 | + then { |
| 95 | + return true; |
| 96 | + } |
| 97 | + } |
| 98 | + |
| 99 | + false |
| 100 | +} |
| 101 | + |
| 102 | +fn get_bounds_if_impl_trait<'tcx>(cx: &LateContext<'tcx>, qpath: &QPath<'_>, id: HirId) -> Option<GenericBounds<'tcx>> { |
| 103 | + if_chain! { |
| 104 | + if let Some(did) = cx.qpath_res(qpath, id).opt_def_id(); |
| 105 | + if let Some(Node::GenericParam(generic_param)) = cx.tcx.hir().get_if_local(did); |
| 106 | + if let GenericParamKind::Type { synthetic, .. } = generic_param.kind; |
| 107 | + if synthetic == Some(SyntheticTyParamKind::ImplTrait); |
| 108 | + then { |
| 109 | + Some(generic_param.bounds) |
| 110 | + } else { |
| 111 | + None |
| 112 | + } |
| 113 | + } |
| 114 | +} |
0 commit comments