|
| 1 | +use clippy_utils::diagnostics::span_lint_and_sugg; |
| 2 | +use clippy_utils::res::MaybeDef; |
| 3 | +use clippy_utils::source::snippet_with_applicability; |
| 4 | +use clippy_utils::sugg::Sugg; |
| 5 | +use clippy_utils::sym; |
| 6 | +use clippy_utils::ty::peel_and_count_ty_refs; |
| 7 | +use rustc_ast::{BorrowKind, UnOp}; |
| 8 | +use rustc_errors::Applicability; |
| 9 | +use rustc_hir::{Expr, ExprKind, LangItem}; |
| 10 | +use rustc_lint::LateContext; |
| 11 | +use rustc_middle::ty::Ty; |
| 12 | +use rustc_span::{Span, Symbol}; |
| 13 | + |
| 14 | +use super::CLONES_INTO_BOXED_SLICES; |
| 15 | + |
| 16 | +// Shows the lint with a suggestion using the given parts |
| 17 | +// Assures that the inner argument is correctly ref'd/deref'd in the suggestion based on needs_ref |
| 18 | +fn show_lint( |
| 19 | + cx: &LateContext<'_>, |
| 20 | + full_span: Span, |
| 21 | + mut inner: &Expr<'_>, |
| 22 | + needs_ref: bool, |
| 23 | + suggestion: (Option<&str>, &str, Option<&str>), |
| 24 | + degrade_app_to: Option<Applicability>, |
| 25 | +) { |
| 26 | + let mut applicability = degrade_app_to.unwrap_or(Applicability::MachineApplicable); |
| 27 | + |
| 28 | + while let ExprKind::AddrOf(BorrowKind::Ref, _, expr) | ExprKind::Unary(UnOp::Deref, expr) = inner.kind { |
| 29 | + inner = expr; |
| 30 | + } |
| 31 | + |
| 32 | + let mut sugg = Sugg::hir_with_context(cx, inner, full_span.ctxt(), suggestion.1, &mut applicability); |
| 33 | + |
| 34 | + let inner_ty = cx.typeck_results().expr_ty(inner); |
| 35 | + let (inner_ty_peeled, ref_count, _) = peel_and_count_ty_refs(inner_ty); |
| 36 | + let mut ref_count = ref_count as i128; |
| 37 | + if needs_ref { |
| 38 | + if ty_is_slice_like(cx, inner_ty_peeled) { |
| 39 | + ref_count -= 1; |
| 40 | + } else { |
| 41 | + // Inner argument is in some kind of Rc-like object, so it should be addr_deref'd to get a reference |
| 42 | + // to the underlying slice |
| 43 | + sugg = sugg.addr_deref(); |
| 44 | + } |
| 45 | + } |
| 46 | + for _ in 0..ref_count { |
| 47 | + sugg = sugg.deref(); |
| 48 | + } |
| 49 | + for _ in 0..-ref_count { |
| 50 | + sugg = sugg.addr(); |
| 51 | + } |
| 52 | + |
| 53 | + span_lint_and_sugg( |
| 54 | + cx, |
| 55 | + CLONES_INTO_BOXED_SLICES, |
| 56 | + full_span, |
| 57 | + "clone into boxed slice", |
| 58 | + "use", |
| 59 | + format!( |
| 60 | + "Box::from({}{}{})", |
| 61 | + suggestion.0.unwrap_or_default(), |
| 62 | + sugg, |
| 63 | + suggestion.2.unwrap_or_default() |
| 64 | + ), |
| 65 | + applicability, |
| 66 | + ); |
| 67 | +} |
| 68 | + |
| 69 | +// Is the given type a slice, path, or one of the str types |
| 70 | +fn ty_is_slice_like(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { |
| 71 | + ty.is_slice() || ty.is_str() || matches!(ty.opt_diag_name(&cx.tcx), Some(sym::cstr_type | sym::Path | sym::OsStr)) |
| 72 | +} |
| 73 | + |
| 74 | +// Shows the lint with a suggestion that depends on the types of the inner argument and the |
| 75 | +// resulting Box |
| 76 | +pub(super) fn check( |
| 77 | + cx: &LateContext<'_>, |
| 78 | + first_method_ty: Ty<'_>, |
| 79 | + second_method_name: Symbol, |
| 80 | + full_span: Span, |
| 81 | + arg: &Expr<'_>, |
| 82 | +) { |
| 83 | + let first_ty_diag_name = first_method_ty.opt_diag_name(cx); |
| 84 | + if (second_method_name == sym::into_boxed_c_str && first_ty_diag_name != Some(sym::cstring_type)) |
| 85 | + || (second_method_name == sym::into_boxed_os_str && first_ty_diag_name != Some(sym::OsString)) |
| 86 | + || (second_method_name == sym::into_boxed_path && first_ty_diag_name != Some(sym::PathBuf)) |
| 87 | + || (second_method_name == sym::into_boxed_str && !first_method_ty.is_lang_item(cx, LangItem::String)) |
| 88 | + || (second_method_name == sym::into_boxed_slice && first_ty_diag_name != Some(sym::Vec)) |
| 89 | + { |
| 90 | + return; |
| 91 | + } |
| 92 | + |
| 93 | + let arg_ty = cx.typeck_results().expr_ty(arg); |
| 94 | + let inner_ty = arg_ty.peel_refs(); |
| 95 | + if ty_is_slice_like(cx, inner_ty) { |
| 96 | + if second_method_name == sym::into_boxed_path && !inner_ty.is_diag_item(cx, sym::Path) { |
| 97 | + // PathBuf's from(...) can convert from other str types, |
| 98 | + // so Path::new(...) must be used to assure resulting Box is the correct type |
| 99 | + show_lint( |
| 100 | + cx, |
| 101 | + full_span, |
| 102 | + arg, |
| 103 | + true, |
| 104 | + (Some("Path::new("), "...", Some(")")), |
| 105 | + Some(Applicability::MaybeIncorrect), |
| 106 | + ); |
| 107 | + } else if let ExprKind::Unary(UnOp::Deref, deref_inner) = arg.kind |
| 108 | + && cx |
| 109 | + .typeck_results() |
| 110 | + .expr_ty(deref_inner) |
| 111 | + .is_lang_item(cx, LangItem::OwnedBox) |
| 112 | + { |
| 113 | + // Special case when inner argument is already in a Box: just use Box::clone |
| 114 | + let mut applicability = Applicability::MachineApplicable; |
| 115 | + span_lint_and_sugg( |
| 116 | + cx, |
| 117 | + CLONES_INTO_BOXED_SLICES, |
| 118 | + full_span, |
| 119 | + "clone into boxed slice", |
| 120 | + "use", |
| 121 | + format!( |
| 122 | + "{}.clone()", |
| 123 | + snippet_with_applicability(cx, deref_inner.span, "...", &mut applicability) |
| 124 | + ), |
| 125 | + applicability, |
| 126 | + ); |
| 127 | + } else { |
| 128 | + // Inner type is a ref to a slice, so it can be directly used in the suggestion |
| 129 | + show_lint(cx, full_span, arg, true, (None, "...", None), None); |
| 130 | + } |
| 131 | + // For all the following the inner type is owned, so they have to be converted to a |
| 132 | + // reference first for the suggestion |
| 133 | + } else if inner_ty.is_lang_item(cx, LangItem::String) { |
| 134 | + show_lint(cx, full_span, arg, false, (None, "(...)", Some(".as_str()")), None); |
| 135 | + } else if inner_ty.is_diag_item(cx, sym::cstring_type) { |
| 136 | + show_lint(cx, full_span, arg, false, (None, "(...)", Some(".as_c_str()")), None); |
| 137 | + } else if inner_ty.is_diag_item(cx, sym::PathBuf) { |
| 138 | + show_lint(cx, full_span, arg, false, (None, "(...)", Some(".as_path()")), None); |
| 139 | + } else if inner_ty.is_diag_item(cx, sym::Vec) { |
| 140 | + show_lint(cx, full_span, arg, false, (Some("&"), "(...)", Some("[..]")), None); |
| 141 | + } else if inner_ty.is_diag_item(cx, sym::OsString) { |
| 142 | + show_lint(cx, full_span, arg, false, (None, "(...)", Some(".as_os_str()")), None); |
| 143 | + } |
| 144 | +} |
0 commit comments