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