]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | use crate::{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 { None } else { Some(visitor.spans) } | |
40 | } | |
41 | ||
42 | struct PtrCloneVisitor<'a, 'tcx> { | |
43 | cx: &'a LateContext<'tcx>, | |
44 | name: Symbol, | |
45 | replace: &'a [(&'static str, &'static str)], | |
46 | spans: Vec<(Span, Cow<'static, str>)>, | |
47 | abort: bool, | |
48 | } | |
49 | ||
50 | impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> { | |
51 | type Map = Map<'tcx>; | |
52 | ||
53 | fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { | |
54 | if self.abort { | |
55 | return; | |
56 | } | |
57 | if let ExprKind::MethodCall(ref seg, _, ref args, _) = expr.kind { | |
58 | if args.len() == 1 && match_var(&args[0], self.name) { | |
59 | if seg.ident.name.as_str() == "capacity" { | |
60 | self.abort = true; | |
61 | return; | |
62 | } | |
63 | for &(fn_name, suffix) in self.replace { | |
64 | if seg.ident.name.as_str() == fn_name { | |
65 | self.spans | |
66 | .push((expr.span, snippet(self.cx, args[0].span, "_") + suffix)); | |
67 | return; | |
68 | } | |
69 | } | |
70 | } | |
71 | } | |
72 | walk_expr(self, expr); | |
73 | } | |
74 | ||
75 | fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { | |
76 | NestedVisitorMap::None | |
77 | } | |
78 | } | |
79 | ||
80 | fn get_binding_name(arg: &Param<'_>) -> Option<Symbol> { | |
81 | get_pat_name(&arg.pat) | |
82 | } |