]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/lifetimes.rs
Remove import of rustc
[rust.git] / clippy_lints / src / lifetimes.rs
1 use crate::reexport::*;
2 use rustc::lint::*;
3 use rustc::{declare_lint, lint_array};
4 use rustc::hir::def::Def;
5 use rustc::hir::*;
6 use rustc::hir::intravisit::*;
7 use std::collections::{HashMap, HashSet};
8 use syntax::codemap::Span;
9 use crate::utils::{in_external_macro, last_path_segment, span_lint};
10 use syntax::symbol::keywords;
11
12 /// **What it does:** Checks for lifetime annotations which can be removed by
13 /// relying on lifetime elision.
14 ///
15 /// **Why is this bad?** The additional lifetimes make the code look more
16 /// complicated, while there is nothing out of the ordinary going on. Removing
17 /// them leads to more readable code.
18 ///
19 /// **Known problems:** Potential false negatives: we bail out if the function
20 /// has a `where` clause where lifetimes are mentioned.
21 ///
22 /// **Example:**
23 /// ```rust
24 /// fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 { x }
25 /// ```
26 declare_clippy_lint! {
27     pub NEEDLESS_LIFETIMES,
28     complexity,
29     "using explicit lifetimes for references in function arguments when elision rules \
30      would allow omitting them"
31 }
32
33 /// **What it does:** Checks for lifetimes in generics that are never used
34 /// anywhere else.
35 ///
36 /// **Why is this bad?** The additional lifetimes make the code look more
37 /// complicated, while there is nothing out of the ordinary going on. Removing
38 /// them leads to more readable code.
39 ///
40 /// **Known problems:** None.
41 ///
42 /// **Example:**
43 /// ```rust
44 /// fn unused_lifetime<'a>(x: u8) { .. }
45 /// ```
46 declare_clippy_lint! {
47     pub EXTRA_UNUSED_LIFETIMES,
48     complexity,
49     "unused lifetimes in function definitions"
50 }
51
52 #[derive(Copy, Clone)]
53 pub struct LifetimePass;
54
55 impl LintPass for LifetimePass {
56     fn get_lints(&self) -> LintArray {
57         lint_array!(NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES)
58     }
59 }
60
61 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LifetimePass {
62     fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
63         if let ItemKind::Fn(ref decl, _, ref generics, id) = item.node {
64             check_fn_inner(cx, decl, Some(id), generics, item.span);
65         }
66     }
67
68     fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx ImplItem) {
69         if let ImplItemKind::Method(ref sig, id) = item.node {
70             check_fn_inner(cx, &sig.decl, Some(id), &item.generics, item.span);
71         }
72     }
73
74     fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem) {
75         if let TraitItemKind::Method(ref sig, ref body) = item.node {
76             let body = match *body {
77                 TraitMethod::Required(_) => None,
78                 TraitMethod::Provided(id) => Some(id),
79             };
80             check_fn_inner(cx, &sig.decl, body, &item.generics, item.span);
81         }
82     }
83 }
84
85 /// The lifetime of a &-reference.
86 #[derive(PartialEq, Eq, Hash, Debug)]
87 enum RefLt {
88     Unnamed,
89     Static,
90     Named(Name),
91 }
92
93 fn check_fn_inner<'a, 'tcx>(
94     cx: &LateContext<'a, 'tcx>,
95     decl: &'tcx FnDecl,
96     body: Option<BodyId>,
97     generics: &'tcx Generics,
98     span: Span,
99 ) {
100     if in_external_macro(cx, span) || has_where_lifetimes(cx, &generics.where_clause) {
101         return;
102     }
103
104     let mut bounds_lts = Vec::new();
105     let types = generics.params.iter().filter_map(|param| match param.kind {
106         GenericParamKind::Type { .. } => Some(param),
107         GenericParamKind::Lifetime { .. } => None,
108     });
109     for typ in types {
110         for bound in &typ.bounds {
111             let mut visitor = RefVisitor::new(cx);
112             walk_param_bound(&mut visitor, bound);
113             if visitor.lts.iter().any(|lt| matches!(lt, RefLt::Named(_))) {
114                 return;
115             }
116             if let GenericBound::Trait(ref trait_ref, _) = *bound {
117                 let params = &trait_ref
118                     .trait_ref
119                     .path
120                     .segments
121                     .last()
122                     .expect("a path must have at least one segment")
123                     .args;
124                 if let Some(ref params) = *params {
125                     let lifetimes = params.args.iter().filter_map(|arg| match arg {
126                         GenericArg::Lifetime(lt) => Some(lt),
127                         GenericArg::Type(_) => None,
128                     });
129                     for bound in lifetimes {
130                         if bound.name != LifetimeName::Static && !bound.is_elided() {
131                             return;
132                         }
133                         bounds_lts.push(bound);
134                     }
135                 }
136             }
137         }
138     }
139     if could_use_elision(cx, decl, body, &generics.params, bounds_lts) {
140         span_lint(
141             cx,
142             NEEDLESS_LIFETIMES,
143             span,
144             "explicit lifetimes given in parameter types where they could be elided",
145         );
146     }
147     report_extra_lifetimes(cx, decl, generics);
148 }
149
150 fn could_use_elision<'a, 'tcx: 'a>(
151     cx: &LateContext<'a, 'tcx>,
152     func: &'tcx FnDecl,
153     body: Option<BodyId>,
154     named_generics: &'tcx [GenericParam],
155     bounds_lts: Vec<&'tcx Lifetime>,
156 ) -> bool {
157     // There are two scenarios where elision works:
158     // * no output references, all input references have different LT
159     // * output references, exactly one input reference with same LT
160     // All lifetimes must be unnamed, 'static or defined without bounds on the
161     // level of the current item.
162
163     // check named LTs
164     let allowed_lts = allowed_lts_from(named_generics);
165
166     // these will collect all the lifetimes for references in arg/return types
167     let mut input_visitor = RefVisitor::new(cx);
168     let mut output_visitor = RefVisitor::new(cx);
169
170     // extract lifetimes in input argument types
171     for arg in &func.inputs {
172         input_visitor.visit_ty(arg);
173     }
174     // extract lifetimes in output type
175     if let Return(ref ty) = func.output {
176         output_visitor.visit_ty(ty);
177     }
178
179     let input_lts = match input_visitor.into_vec() {
180         Some(lts) => lts_from_bounds(lts, bounds_lts.into_iter()),
181         None => return false,
182     };
183     let output_lts = match output_visitor.into_vec() {
184         Some(val) => val,
185         None => return false,
186     };
187
188     if let Some(body_id) = body {
189         let mut checker = BodyLifetimeChecker {
190             lifetimes_used_in_body: false,
191         };
192         checker.visit_expr(&cx.tcx.hir.body(body_id).value);
193         if checker.lifetimes_used_in_body {
194             return false;
195         }
196     }
197
198     // check for lifetimes from higher scopes
199     for lt in input_lts.iter().chain(output_lts.iter()) {
200         if !allowed_lts.contains(lt) {
201             return false;
202         }
203     }
204
205     // no input lifetimes? easy case!
206     if input_lts.is_empty() {
207         false
208     } else if output_lts.is_empty() {
209         // no output lifetimes, check distinctness of input lifetimes
210
211         // only unnamed and static, ok
212         let unnamed_and_static = input_lts
213             .iter()
214             .all(|lt| *lt == RefLt::Unnamed || *lt == RefLt::Static);
215         if unnamed_and_static {
216             return false;
217         }
218         // we have no output reference, so we only need all distinct lifetimes
219         input_lts.len() == unique_lifetimes(&input_lts)
220     } else {
221         // we have output references, so we need one input reference,
222         // and all output lifetimes must be the same
223         if unique_lifetimes(&output_lts) > 1 {
224             return false;
225         }
226         if input_lts.len() == 1 {
227             match (&input_lts[0], &output_lts[0]) {
228                 (&RefLt::Named(n1), &RefLt::Named(n2)) if n1 == n2 => true,
229                 (&RefLt::Named(_), &RefLt::Unnamed) => true,
230                 _ => false, /* already elided, different named lifetimes
231                              * or something static going on */
232             }
233         } else {
234             false
235         }
236     }
237 }
238
239 fn allowed_lts_from(named_generics: &[GenericParam]) -> HashSet<RefLt> {
240     let mut allowed_lts = HashSet::new();
241     for par in named_generics.iter() {
242         if let GenericParamKind::Lifetime { .. } = par.kind {
243             if par.bounds.is_empty() {
244                 allowed_lts.insert(RefLt::Named(par.name.ident().name));
245             }
246         }
247     }
248     allowed_lts.insert(RefLt::Unnamed);
249     allowed_lts.insert(RefLt::Static);
250     allowed_lts
251 }
252
253 fn lts_from_bounds<'a, T: Iterator<Item = &'a Lifetime>>(mut vec: Vec<RefLt>, bounds_lts: T) -> Vec<RefLt> {
254     for lt in bounds_lts {
255         if lt.name != LifetimeName::Static {
256             vec.push(RefLt::Named(lt.name.ident().name));
257         }
258     }
259
260     vec
261 }
262
263 /// Number of unique lifetimes in the given vector.
264 fn unique_lifetimes(lts: &[RefLt]) -> usize {
265     lts.iter().collect::<HashSet<_>>().len()
266 }
267
268 /// A visitor usable for `rustc_front::visit::walk_ty()`.
269 struct RefVisitor<'a, 'tcx: 'a> {
270     cx: &'a LateContext<'a, 'tcx>,
271     lts: Vec<RefLt>,
272     abort: bool,
273 }
274
275 impl<'v, 't> RefVisitor<'v, 't> {
276     fn new(cx: &'v LateContext<'v, 't>) -> Self {
277         Self {
278             cx,
279             lts: Vec::new(),
280             abort: false,
281         }
282     }
283
284     fn record(&mut self, lifetime: &Option<Lifetime>) {
285         if let Some(ref lt) = *lifetime {
286             if lt.name == LifetimeName::Static {
287                 self.lts.push(RefLt::Static);
288             } else if lt.is_elided() {
289                 self.lts.push(RefLt::Unnamed);
290             } else {
291                 self.lts.push(RefLt::Named(lt.name.ident().name));
292             }
293         } else {
294             self.lts.push(RefLt::Unnamed);
295         }
296     }
297
298     fn into_vec(self) -> Option<Vec<RefLt>> {
299         if self.abort {
300             None
301         } else {
302             Some(self.lts)
303         }
304     }
305
306     fn collect_anonymous_lifetimes(&mut self, qpath: &QPath, ty: &Ty) {
307         if let Some(ref last_path_segment) = last_path_segment(qpath).args {
308             if !last_path_segment.parenthesized
309                 && !last_path_segment.args.iter().any(|arg| match arg {
310                     GenericArg::Lifetime(_) => true,
311                     GenericArg::Type(_) => false,
312                 }) {
313                 let hir_id = self.cx.tcx.hir.node_to_hir_id(ty.id);
314                 match self.cx.tables.qpath_def(qpath, hir_id) {
315                     Def::TyAlias(def_id) | Def::Struct(def_id) => {
316                         let generics = self.cx.tcx.generics_of(def_id);
317                         for _ in generics.params.as_slice() {
318                             self.record(&None);
319                         }
320                     },
321                     Def::Trait(def_id) => {
322                         let trait_def = self.cx.tcx.trait_def(def_id);
323                         for _ in &self.cx.tcx.generics_of(trait_def.def_id).params {
324                             self.record(&None);
325                         }
326                     },
327                     _ => (),
328                 }
329             }
330         }
331     }
332 }
333
334 impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
335     // for lifetimes as parameters of generics
336     fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
337         self.record(&Some(*lifetime));
338     }
339
340     fn visit_ty(&mut self, ty: &'tcx Ty) {
341         match ty.node {
342             TyKind::Rptr(ref lt, _) if lt.is_elided() => {
343                 self.record(&None);
344             },
345             TyKind::Path(ref path) => {
346                 if let QPath::Resolved(_, ref path) = *path {
347                     if let Def::Existential(def_id) = path.def {
348                         let node_id = self.cx.tcx.hir.as_local_node_id(def_id).unwrap();
349                         if let ItemKind::Existential(ref exist_ty) = self.cx.tcx.hir.expect_item(node_id).node {
350                             for bound in &exist_ty.bounds {
351                                 if let GenericBound::Outlives(_) = *bound {
352                                     self.record(&None);
353                                 }
354                             }
355                         } else {
356                             unreachable!()
357                         }
358                         walk_ty(self, ty);
359                         return;
360                     }
361                 }
362                 self.collect_anonymous_lifetimes(path, ty);
363             }
364             TyKind::TraitObject(ref bounds, ref lt) => {
365                 if !lt.is_elided() {
366                     self.abort = true;
367                 }
368                 for bound in bounds {
369                     self.visit_poly_trait_ref(bound, TraitBoundModifier::None);
370                 }
371                 return;
372             },
373             _ => (),
374         }
375         walk_ty(self, ty);
376     }
377     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
378         NestedVisitorMap::None
379     }
380 }
381
382 /// Are any lifetimes mentioned in the `where` clause? If yes, we don't try to
383 /// reason about elision.
384 fn has_where_lifetimes<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, where_clause: &'tcx WhereClause) -> bool {
385     for predicate in &where_clause.predicates {
386         match *predicate {
387             WherePredicate::RegionPredicate(..) => return true,
388             WherePredicate::BoundPredicate(ref pred) => {
389                 // a predicate like F: Trait or F: for<'a> Trait<'a>
390                 let mut visitor = RefVisitor::new(cx);
391                 // walk the type F, it may not contain LT refs
392                 walk_ty(&mut visitor, &pred.bounded_ty);
393                 if !visitor.lts.is_empty() {
394                     return true;
395                 }
396                 // if the bounds define new lifetimes, they are fine to occur
397                 let allowed_lts = allowed_lts_from(&pred.bound_generic_params);
398                 // now walk the bounds
399                 for bound in pred.bounds.iter() {
400                     walk_param_bound(&mut visitor, bound);
401                 }
402                 // and check that all lifetimes are allowed
403                 match visitor.into_vec() {
404                     None => return false,
405                     Some(lts) => for lt in lts {
406                         if !allowed_lts.contains(&lt) {
407                             return true;
408                         }
409                     },
410                 }
411             },
412             WherePredicate::EqPredicate(ref pred) => {
413                 let mut visitor = RefVisitor::new(cx);
414                 walk_ty(&mut visitor, &pred.lhs_ty);
415                 walk_ty(&mut visitor, &pred.rhs_ty);
416                 if !visitor.lts.is_empty() {
417                     return true;
418                 }
419             },
420         }
421     }
422     false
423 }
424
425 struct LifetimeChecker {
426     map: HashMap<Name, Span>,
427 }
428
429 impl<'tcx> Visitor<'tcx> for LifetimeChecker {
430     // for lifetimes as parameters of generics
431     fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
432         self.map.remove(&lifetime.name.ident().name);
433     }
434
435     fn visit_generic_param(&mut self, param: &'tcx GenericParam) {
436         // don't actually visit `<'a>` or `<'a: 'b>`
437         // we've already visited the `'a` declarations and
438         // don't want to spuriously remove them
439         // `'b` in `'a: 'b` is useless unless used elsewhere in
440         // a non-lifetime bound
441         if let GenericParamKind::Type { .. } = param.kind {
442             walk_generic_param(self, param)
443         }
444     }
445     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
446         NestedVisitorMap::None
447     }
448 }
449
450 fn report_extra_lifetimes<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, func: &'tcx FnDecl, generics: &'tcx Generics) {
451     let hs = generics.params.iter()
452         .filter_map(|par| match par.kind {
453             GenericParamKind::Lifetime { .. } => Some((par.name.ident().name, par.span)),
454             _ => None,
455         })
456         .collect();
457     let mut checker = LifetimeChecker { map: hs };
458
459     walk_generics(&mut checker, generics);
460     walk_fn_decl(&mut checker, func);
461
462     for &v in checker.map.values() {
463         span_lint(cx, EXTRA_UNUSED_LIFETIMES, v, "this lifetime isn't used in the function definition");
464     }
465 }
466
467 struct BodyLifetimeChecker {
468     lifetimes_used_in_body: bool,
469 }
470
471 impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker {
472     // for lifetimes as parameters of generics
473     fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
474         if lifetime.name.ident().name != keywords::Invalid.name() && lifetime.name.ident().name != "'static" {
475             self.lifetimes_used_in_body = true;
476         }
477     }
478
479     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
480         NestedVisitorMap::None
481     }
482 }