]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/methods/utils.rs
Rollup merge of #105663 - andrewpollack:patch-1, r=tmandry
[rust.git] / src / tools / clippy / clippy_lints / src / methods / utils.rs
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;
5 use rustc_ast::ast;
6 use rustc_errors::Applicability;
7 use rustc_hir as hir;
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;
14
15 pub(super) fn derefs_to_slice<'tcx>(
16     cx: &LateContext<'tcx>,
17     expr: &'tcx hir::Expr<'tcx>,
18     ty: Ty<'tcx>,
19 ) -> Option<&'tcx hir::Expr<'tcx>> {
20     fn may_slice<'a>(cx: &LateContext<'a>, ty: Ty<'a>) -> bool {
21         match ty.kind() {
22             ty::Slice(_) => true,
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),
27             _ => false,
28         }
29     }
30
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)) {
33             Some(self_arg)
34         } else {
35             None
36         }
37     } else {
38         match ty.kind() {
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) {
43                     Some(expr)
44                 } else {
45                     None
46                 }
47             },
48             _ => None,
49         }
50     }
51 }
52
53 pub(super) fn get_hint_if_single_char_arg(
54     cx: &LateContext<'_>,
55     arg: &hir::Expr<'_>,
56     applicability: &mut Applicability,
57 ) -> Option<String> {
58     if_chain! {
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;
63         then {
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)]
69             } else {
70                 // for regular string: "a"
71                 &snip[1..(snip.len() - 1)]
72             };
73
74             let hint = format!("'{}'", match ch {
75                 "'" => "\\'" ,
76                 r"\" => "\\\\",
77                 _ => ch,
78             });
79
80             Some(hint)
81         } else {
82             None
83         }
84     }
85 }
86
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>,
91     pat: &Pat<'tcx>,
92     body: &'tcx Expr<'tcx>,
93 ) -> (bool, Vec<&'tcx Expr<'tcx>>) {
94     let mut visitor = CloneOrCopyVisitor {
95         cx,
96         binding_hir_ids: pat_bindings(pat),
97         clone_or_copy_needed: false,
98         addr_of_exprs: Vec::new(),
99     };
100     visitor.visit_expr(body);
101     (visitor.clone_or_copy_needed, visitor.addr_of_exprs)
102 }
103
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(),
108     };
109     collector.visit_pat(pat);
110     collector.binding_hir_ids
111 }
112
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>>,
124 }
125
126 impl<'cx, 'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'cx, 'tcx> {
127     type NestedFilter = nested_filter::OnlyBodies;
128
129     fn nested_visit_map(&mut self) -> Self::Map {
130         self.cx.tcx.hir()
131     }
132
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) {
137                 match parent.kind {
138                     ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) => {
139                         self.addr_of_exprs.push(parent);
140                         return;
141                     },
142                     ExprKind::MethodCall(.., args, _) => {
143                         if_chain! {
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));
149                             then {
150                                 return;
151                             }
152                         }
153                     },
154                     _ => {},
155                 }
156             }
157             self.clone_or_copy_needed = true;
158         }
159     }
160 }
161
162 impl<'cx, 'tcx> CloneOrCopyVisitor<'cx, 'tcx> {
163     fn is_binding(&self, expr: &Expr<'tcx>) -> bool {
164         self.binding_hir_ids
165             .iter()
166             .any(|hir_id| path_to_local_id(expr, *hir_id))
167     }
168 }