]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/utils/ptr.rs
Auto merge of #6701 - camsteffen:collapsible-if, r=flip1995
[rust.git] / clippy_lints / src / utils / ptr.rs
1 use crate::utils::{get_pat_name, match_var, snippet};
2 use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
3 use rustc_hir::{Body, BodyId, Expr, ExprKind, Param};
4 use rustc_lint::LateContext;
5 use rustc_middle::hir::map::Map;
6 use rustc_span::{Span, Symbol};
7 use std::borrow::Cow;
8
9 pub fn get_spans(
10     cx: &LateContext<'_>,
11     opt_body_id: Option<BodyId>,
12     idx: usize,
13     replacements: &[(&'static str, &'static str)],
14 ) -> Option<Vec<(Span, Cow<'static, str>)>> {
15     if let Some(body) = opt_body_id.map(|id| cx.tcx.hir().body(id)) {
16         get_binding_name(&body.params[idx]).map_or_else(
17             || Some(vec![]),
18             |name| extract_clone_suggestions(cx, name, replacements, body),
19         )
20     } else {
21         Some(vec![])
22     }
23 }
24
25 fn extract_clone_suggestions<'tcx>(
26     cx: &LateContext<'tcx>,
27     name: Symbol,
28     replace: &[(&'static str, &'static str)],
29     body: &'tcx Body<'_>,
30 ) -> Option<Vec<(Span, Cow<'static, str>)>> {
31     let mut visitor = PtrCloneVisitor {
32         cx,
33         name,
34         replace,
35         spans: vec![],
36         abort: false,
37     };
38     visitor.visit_body(body);
39     if visitor.abort {
40         None
41     } else {
42         Some(visitor.spans)
43     }
44 }
45
46 struct PtrCloneVisitor<'a, 'tcx> {
47     cx: &'a LateContext<'tcx>,
48     name: Symbol,
49     replace: &'a [(&'static str, &'static str)],
50     spans: Vec<(Span, Cow<'static, str>)>,
51     abort: bool,
52 }
53
54 impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> {
55     type Map = Map<'tcx>;
56
57     fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
58         if self.abort {
59             return;
60         }
61         if let ExprKind::MethodCall(ref seg, _, ref args, _) = expr.kind {
62             if args.len() == 1 && match_var(&args[0], self.name) {
63                 if seg.ident.name.as_str() == "capacity" {
64                     self.abort = true;
65                     return;
66                 }
67                 for &(fn_name, suffix) in self.replace {
68                     if seg.ident.name.as_str() == fn_name {
69                         self.spans
70                             .push((expr.span, snippet(self.cx, args[0].span, "_") + suffix));
71                         return;
72                     }
73                 }
74             }
75         }
76         walk_expr(self, expr);
77     }
78
79     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
80         NestedVisitorMap::None
81     }
82 }
83
84 fn get_binding_name(arg: &Param<'_>) -> Option<Symbol> {
85     get_pat_name(&arg.pat)
86 }