| use clippy_utils::source::snippet_with_applicability; |
| use clippy_utils::ty::is_type_diagnostic_item; |
| use clippy_utils::{get_parent_expr, path_to_local_id, usage}; |
| use if_chain::if_chain; |
| use rustc_ast::ast; |
| use rustc_errors::Applicability; |
| use rustc_hir as hir; |
| use rustc_hir::intravisit::{walk_expr, Visitor}; |
| use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Pat}; |
| use rustc_lint::LateContext; |
| use rustc_middle::hir::nested_filter; |
| use rustc_middle::ty::{self, Ty}; |
| use rustc_span::symbol::sym; |
| |
| pub(super) fn derefs_to_slice<'tcx>( |
| cx: &LateContext<'tcx>, |
| expr: &'tcx hir::Expr<'tcx>, |
| ty: Ty<'tcx>, |
| ) -> Option<&'tcx hir::Expr<'tcx>> { |
| fn may_slice<'a>(cx: &LateContext<'a>, ty: Ty<'a>) -> bool { |
| match ty.kind() { |
| ty::Slice(_) => true, |
| ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()), |
| ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::Vec), |
| ty::Array(_, size) => size.try_eval_target_usize(cx.tcx, cx.param_env).is_some(), |
| ty::Ref(_, inner, _) => may_slice(cx, *inner), |
| _ => false, |
| } |
| } |
| |
| if let hir::ExprKind::MethodCall(path, self_arg, ..) = &expr.kind { |
| if path.ident.name == sym::iter && may_slice(cx, cx.typeck_results().expr_ty(self_arg)) { |
| Some(self_arg) |
| } else { |
| None |
| } |
| } else { |
| match ty.kind() { |
| ty::Slice(_) => Some(expr), |
| ty::Adt(def, _) if def.is_box() && may_slice(cx, ty.boxed_ty()) => Some(expr), |
| ty::Ref(_, inner, _) => { |
| if may_slice(cx, *inner) { |
| Some(expr) |
| } else { |
| None |
| } |
| }, |
| _ => None, |
| } |
| } |
| } |
| |
| pub(super) fn get_hint_if_single_char_arg( |
| cx: &LateContext<'_>, |
| arg: &hir::Expr<'_>, |
| applicability: &mut Applicability, |
| ) -> Option<String> { |
| if_chain! { |
| if let hir::ExprKind::Lit(lit) = &arg.kind; |
| if let ast::LitKind::Str(r, style) = lit.node; |
| let string = r.as_str(); |
| if string.chars().count() == 1; |
| then { |
| let snip = snippet_with_applicability(cx, arg.span, string, applicability); |
| let ch = if let ast::StrStyle::Raw(nhash) = style { |
| let nhash = nhash as usize; |
| // for raw string: r##"a"## |
| &snip[(nhash + 2)..(snip.len() - 1 - nhash)] |
| } else { |
| // for regular string: "a" |
| &snip[1..(snip.len() - 1)] |
| }; |
| |
| let hint = format!("'{}'", match ch { |
| "'" => "\\'" , |
| r"\" => "\\\\", |
| _ => ch, |
| }); |
| |
| Some(hint) |
| } else { |
| None |
| } |
| } |
| } |
| |
| /// The core logic of `check_for_loop_iter` in `unnecessary_iter_cloned.rs`, this function wraps a |
| /// use of `CloneOrCopyVisitor`. |
| pub(super) fn clone_or_copy_needed<'tcx>( |
| cx: &LateContext<'tcx>, |
| pat: &Pat<'tcx>, |
| body: &'tcx Expr<'tcx>, |
| ) -> (bool, Vec<&'tcx Expr<'tcx>>) { |
| let mut visitor = CloneOrCopyVisitor { |
| cx, |
| binding_hir_ids: pat_bindings(pat), |
| clone_or_copy_needed: false, |
| addr_of_exprs: Vec::new(), |
| }; |
| visitor.visit_expr(body); |
| (visitor.clone_or_copy_needed, visitor.addr_of_exprs) |
| } |
| |
| /// Returns a vector of all `HirId`s bound by the pattern. |
| fn pat_bindings(pat: &Pat<'_>) -> Vec<HirId> { |
| let mut collector = usage::ParamBindingIdCollector { |
| binding_hir_ids: Vec::new(), |
| }; |
| collector.visit_pat(pat); |
| collector.binding_hir_ids |
| } |
| |
| /// `clone_or_copy_needed` will be false when `CloneOrCopyVisitor` is done visiting if the only |
| /// operations performed on `binding_hir_ids` are: |
| /// * to take non-mutable references to them |
| /// * to use them as non-mutable `&self` in method calls |
| /// If any of `binding_hir_ids` is used in any other way, then `clone_or_copy_needed` will be true |
| /// when `CloneOrCopyVisitor` is done visiting. |
| struct CloneOrCopyVisitor<'cx, 'tcx> { |
| cx: &'cx LateContext<'tcx>, |
| binding_hir_ids: Vec<HirId>, |
| clone_or_copy_needed: bool, |
| addr_of_exprs: Vec<&'tcx Expr<'tcx>>, |
| } |
| |
| impl<'cx, 'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'cx, 'tcx> { |
| type NestedFilter = nested_filter::OnlyBodies; |
| |
| fn nested_visit_map(&mut self) -> Self::Map { |
| self.cx.tcx.hir() |
| } |
| |
| fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { |
| walk_expr(self, expr); |
| if self.is_binding(expr) { |
| if let Some(parent) = get_parent_expr(self.cx, expr) { |
| match parent.kind { |
| ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) => { |
| self.addr_of_exprs.push(parent); |
| return; |
| }, |
| ExprKind::MethodCall(.., args, _) => { |
| if_chain! { |
| if args.iter().all(|arg| !self.is_binding(arg)); |
| if let Some(method_def_id) = self.cx.typeck_results().type_dependent_def_id(parent.hir_id); |
| let method_ty = self.cx.tcx.type_of(method_def_id).instantiate_identity(); |
| let self_ty = method_ty.fn_sig(self.cx.tcx).input(0).skip_binder(); |
| if matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Not)); |
| then { |
| return; |
| } |
| } |
| }, |
| _ => {}, |
| } |
| } |
| self.clone_or_copy_needed = true; |
| } |
| } |
| } |
| |
| impl<'cx, 'tcx> CloneOrCopyVisitor<'cx, 'tcx> { |
| fn is_binding(&self, expr: &Expr<'tcx>) -> bool { |
| self.binding_hir_ids |
| .iter() |
| .any(|hir_id| path_to_local_id(expr, *hir_id)) |
| } |
| } |