1 use clippy_utils::source::snippet_with_applicability;
2 use clippy_utils::ty::is_type_diagnostic_item;
3 use clippy_utils::{get_parent_expr, path_to_local_id, usage};
4 use if_chain::if_chain;
6 use rustc_errors::Applicability;
8 use rustc_hir::intravisit::{walk_expr, Visitor};
9 use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Pat};
10 use rustc_lint::LateContext;
11 use rustc_middle::hir::nested_filter;
12 use rustc_middle::ty::{self, Ty};
13 use rustc_span::symbol::sym;
15 pub(super) fn derefs_to_slice<'tcx>(
16 cx: &LateContext<'tcx>,
17 expr: &'tcx hir::Expr<'tcx>,
19 ) -> Option<&'tcx hir::Expr<'tcx>> {
20 fn may_slice<'a>(cx: &LateContext<'a>, ty: Ty<'a>) -> bool {
23 ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()),
24 ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::Vec),
25 ty::Array(_, size) => size.try_eval_usize(cx.tcx, cx.param_env).is_some(),
26 ty::Ref(_, inner, _) => may_slice(cx, *inner),
31 if let hir::ExprKind::MethodCall(path, self_arg, ..) = &expr.kind {
32 if path.ident.name == sym::iter && may_slice(cx, cx.typeck_results().expr_ty(self_arg)) {
39 ty::Slice(_) => Some(expr),
40 ty::Adt(def, _) if def.is_box() && may_slice(cx, ty.boxed_ty()) => Some(expr),
41 ty::Ref(_, inner, _) => {
42 if may_slice(cx, *inner) {
53 pub(super) fn get_hint_if_single_char_arg(
56 applicability: &mut Applicability,
59 if let hir::ExprKind::Lit(lit) = &arg.kind;
60 if let ast::LitKind::Str(r, style) = lit.node;
61 let string = r.as_str();
62 if string.chars().count() == 1;
64 let snip = snippet_with_applicability(cx, arg.span, string, applicability);
65 let ch = if let ast::StrStyle::Raw(nhash) = style {
66 let nhash = nhash as usize;
67 // for raw string: r##"a"##
68 &snip[(nhash + 2)..(snip.len() - 1 - nhash)]
70 // for regular string: "a"
71 &snip[1..(snip.len() - 1)]
74 let hint = format!("'{}'", match ch {
87 /// The core logic of `check_for_loop_iter` in `unnecessary_iter_cloned.rs`, this function wraps a
88 /// use of `CloneOrCopyVisitor`.
89 pub(super) fn clone_or_copy_needed<'tcx>(
90 cx: &LateContext<'tcx>,
92 body: &'tcx Expr<'tcx>,
93 ) -> (bool, Vec<&'tcx Expr<'tcx>>) {
94 let mut visitor = CloneOrCopyVisitor {
96 binding_hir_ids: pat_bindings(pat),
97 clone_or_copy_needed: false,
98 addr_of_exprs: Vec::new(),
100 visitor.visit_expr(body);
101 (visitor.clone_or_copy_needed, visitor.addr_of_exprs)
104 /// Returns a vector of all `HirId`s bound by the pattern.
105 fn pat_bindings(pat: &Pat<'_>) -> Vec<HirId> {
106 let mut collector = usage::ParamBindingIdCollector {
107 binding_hir_ids: Vec::new(),
109 collector.visit_pat(pat);
110 collector.binding_hir_ids
113 /// `clone_or_copy_needed` will be false when `CloneOrCopyVisitor` is done visiting if the only
114 /// operations performed on `binding_hir_ids` are:
115 /// * to take non-mutable references to them
116 /// * to use them as non-mutable `&self` in method calls
117 /// If any of `binding_hir_ids` is used in any other way, then `clone_or_copy_needed` will be true
118 /// when `CloneOrCopyVisitor` is done visiting.
119 struct CloneOrCopyVisitor<'cx, 'tcx> {
120 cx: &'cx LateContext<'tcx>,
121 binding_hir_ids: Vec<HirId>,
122 clone_or_copy_needed: bool,
123 addr_of_exprs: Vec<&'tcx Expr<'tcx>>,
126 impl<'cx, 'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'cx, 'tcx> {
127 type NestedFilter = nested_filter::OnlyBodies;
129 fn nested_visit_map(&mut self) -> Self::Map {
133 fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
134 walk_expr(self, expr);
135 if self.is_binding(expr) {
136 if let Some(parent) = get_parent_expr(self.cx, expr) {
138 ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) => {
139 self.addr_of_exprs.push(parent);
142 ExprKind::MethodCall(.., args, _) => {
144 if args.iter().all(|arg| !self.is_binding(arg));
145 if let Some(method_def_id) = self.cx.typeck_results().type_dependent_def_id(parent.hir_id);
146 let method_ty = self.cx.tcx.type_of(method_def_id);
147 let self_ty = method_ty.fn_sig(self.cx.tcx).input(0).skip_binder();
148 if matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Not));
157 self.clone_or_copy_needed = true;
162 impl<'cx, 'tcx> CloneOrCopyVisitor<'cx, 'tcx> {
163 fn is_binding(&self, expr: &Expr<'tcx>) -> bool {
166 .any(|hir_id| path_to_local_id(expr, *hir_id))