]> git.lizzy.rs Git - rust.git/blobdiff - clippy_lints/src/lifetimes.rs
Auto merge of #3596 - xfix:remove-crate-from-paths, r=flip1995
[rust.git] / clippy_lints / src / lifetimes.rs
index 684e09e93c418e62cc1a0a52d649b084285da09c..32170c9a7c6a7ce036a794631cc7a406ae77c419 100644 (file)
@@ -1,11 +1,22 @@
-use reexport::*;
-use rustc::lint::*;
+// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use crate::reexport::*;
+use crate::utils::{last_path_segment, span_lint};
+use matches::matches;
 use rustc::hir::def::Def;
-use rustc::hir::*;
 use rustc::hir::intravisit::*;
-use std::collections::{HashMap, HashSet};
-use syntax::codemap::Span;
-use utils::{in_external_macro, last_path_segment, span_lint};
+use rustc::hir::*;
+use rustc::lint::{in_external_macro, LateContext, LateLintPass, LintArray, LintContext, LintPass};
+use rustc::{declare_tool_lint, lint_array};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use syntax::source_map::Span;
 use syntax::symbol::keywords;
 
 /// **What it does:** Checks for lifetime annotations which can be removed by
 ///
 /// **Example:**
 /// ```rust
-/// fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 { x }
+/// fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 {
+///     x
+/// }
 /// ```
-declare_lint! {
-    pub NEEDLESS_LIFETIMES,
-    Warn,
-    "using explicit lifetimes for references in function arguments when elision rules \
    would allow omitting them"
+declare_clippy_lint! {
+pub NEEDLESS_LIFETIMES,
+complexity,
+"using explicit lifetimes for references in function arguments when elision rules \
+ would allow omitting them"
 }
 
 /// **What it does:** Checks for lifetimes in generics that are never used
 ///
 /// **Example:**
 /// ```rust
-/// fn unused_lifetime<'a>(x: u8) { .. }
+/// fn unused_lifetime<'a>(x: u8) {
+///     ..
+/// }
 /// ```
-declare_lint! {
-    pub UNUSED_LIFETIMES,
-    Warn,
+declare_clippy_lint! {
+    pub EXTRA_UNUSED_LIFETIMES,
+    complexity,
     "unused lifetimes in function definitions"
 }
 
 
 impl LintPass for LifetimePass {
     fn get_lints(&self) -> LintArray {
-        lint_array!(NEEDLESS_LIFETIMES, UNUSED_LIFETIMES)
+        lint_array!(NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES)
     }
 }
 
 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LifetimePass {
     fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
-        if let ItemFn(ref decl, _, _, _, ref generics, id) = item.node {
+        if let ItemKind::Fn(ref decl, _, ref generics, id) = item.node {
             check_fn_inner(cx, decl, Some(id), generics, item.span);
         }
     }
@@ -96,24 +111,37 @@ fn check_fn_inner<'a, 'tcx>(
     generics: &'tcx Generics,
     span: Span,
 ) {
-    if in_external_macro(cx, span) || has_where_lifetimes(cx, &generics.where_clause) {
+    if in_external_macro(cx.sess(), span) || has_where_lifetimes(cx, &generics.where_clause) {
         return;
     }
 
     let mut bounds_lts = Vec::new();
-    for typ in generics.ty_params() {
+    let types = generics.params.iter().filter(|param| match param.kind {
+        GenericParamKind::Type { .. } => true,
+        GenericParamKind::Lifetime { .. } => false,
+    });
+    for typ in types {
         for bound in &typ.bounds {
-            if let TraitTyParamBound(ref trait_ref, _) = *bound {
+            let mut visitor = RefVisitor::new(cx);
+            walk_param_bound(&mut visitor, bound);
+            if visitor.lts.iter().any(|lt| matches!(lt, RefLt::Named(_))) {
+                return;
+            }
+            if let GenericBound::Trait(ref trait_ref, _) = *bound {
                 let params = &trait_ref
                     .trait_ref
                     .path
                     .segments
                     .last()
                     .expect("a path must have at least one segment")
-                    .parameters;
+                    .args;
                 if let Some(ref params) = *params {
-                    for bound in &params.lifetimes {
-                        if bound.name.name() != "'static" && !bound.is_elided() {
+                    let lifetimes = params.args.iter().filter_map(|arg| match arg {
+                        GenericArg::Lifetime(lt) => Some(lt),
+                        GenericArg::Type(_) => None,
+                    });
+                    for bound in lifetimes {
+                        if bound.name != LifetimeName::Static && !bound.is_elided() {
                             return;
                         }
                         bounds_lts.push(bound);
@@ -127,7 +155,8 @@ fn check_fn_inner<'a, 'tcx>(
             cx,
             NEEDLESS_LIFETIMES,
             span,
-            "explicit lifetimes given in parameter types where they could be elided",
+            "explicit lifetimes given in parameter types where they could be elided \
+             (or replaced with `'_` if needed by type declaration)",
         );
     }
     report_extra_lifetimes(cx, decl, generics);
@@ -175,7 +204,7 @@ fn could_use_elision<'a, 'tcx: 'a>(
         let mut checker = BodyLifetimeChecker {
             lifetimes_used_in_body: false,
         };
-        checker.visit_expr(&cx.tcx.hir.body(body_id).value);
+        checker.visit_expr(&cx.tcx.hir().body(body_id).value);
         if checker.lifetimes_used_in_body {
             return false;
         }
@@ -195,9 +224,7 @@ fn could_use_elision<'a, 'tcx: 'a>(
         // no output lifetimes, check distinctness of input lifetimes
 
         // only unnamed and static, ok
-        let unnamed_and_static = input_lts
-            .iter()
-            .all(|lt| *lt == RefLt::Unnamed || *lt == RefLt::Static);
+        let unnamed_and_static = input_lts.iter().all(|lt| *lt == RefLt::Unnamed || *lt == RefLt::Static);
         if unnamed_and_static {
             return false;
         }
@@ -222,12 +249,12 @@ fn could_use_elision<'a, 'tcx: 'a>(
     }
 }
 
-fn allowed_lts_from(named_generics: &[GenericParam]) -> HashSet<RefLt> {
-    let mut allowed_lts = HashSet::new();
+fn allowed_lts_from(named_generics: &[GenericParam]) -> FxHashSet<RefLt> {
+    let mut allowed_lts = FxHashSet::default();
     for par in named_generics.iter() {
-        if let GenericParam::Lifetime(ref lt) = *par {
-            if lt.bounds.is_empty() {
-                allowed_lts.insert(RefLt::Named(lt.lifetime.name.name()));
+        if let GenericParamKind::Lifetime { .. } = par.kind {
+            if par.bounds.is_empty() {
+                allowed_lts.insert(RefLt::Named(par.name.ident().name));
             }
         }
     }
@@ -238,8 +265,8 @@ fn allowed_lts_from(named_generics: &[GenericParam]) -> HashSet<RefLt> {
 
 fn lts_from_bounds<'a, T: Iterator<Item = &'a Lifetime>>(mut vec: Vec<RefLt>, bounds_lts: T) -> Vec<RefLt> {
     for lt in bounds_lts {
-        if lt.name.name() != "'static" {
-            vec.push(RefLt::Named(lt.name.name()));
+        if lt.name != LifetimeName::Static {
+            vec.push(RefLt::Named(lt.name.ident().name));
         }
     }
 
@@ -248,7 +275,7 @@ fn lts_from_bounds<'a, T: Iterator<Item = &'a Lifetime>>(mut vec: Vec<RefLt>, bo
 
 /// Number of unique lifetimes in the given vector.
 fn unique_lifetimes(lts: &[RefLt]) -> usize {
-    lts.iter().collect::<HashSet<_>>().len()
+    lts.iter().collect::<FxHashSet<_>>().len()
 }
 
 /// A visitor usable for `rustc_front::visit::walk_ty()`.
@@ -261,7 +288,7 @@ struct RefVisitor<'a, 'tcx: 'a> {
 impl<'v, 't> RefVisitor<'v, 't> {
     fn new(cx: &'v LateContext<'v, 't>) -> Self {
         Self {
-            cx: cx,
+            cx,
             lts: Vec::new(),
             abort: false,
         }
@@ -269,12 +296,12 @@ fn new(cx: &'v LateContext<'v, 't>) -> Self {
 
     fn record(&mut self, lifetime: &Option<Lifetime>) {
         if let Some(ref lt) = *lifetime {
-            if lt.name.name() == "'static" {
+            if lt.name == LifetimeName::Static {
                 self.lts.push(RefLt::Static);
             } else if lt.is_elided() {
                 self.lts.push(RefLt::Unnamed);
             } else {
-                self.lts.push(RefLt::Named(lt.name.name()));
+                self.lts.push(RefLt::Named(lt.name.ident().name));
             }
         } else {
             self.lts.push(RefLt::Unnamed);
@@ -290,19 +317,24 @@ fn into_vec(self) -> Option<Vec<RefLt>> {
     }
 
     fn collect_anonymous_lifetimes(&mut self, qpath: &QPath, ty: &Ty) {
-        if let Some(ref last_path_segment) = last_path_segment(qpath).parameters {
-            if !last_path_segment.parenthesized && last_path_segment.lifetimes.is_empty() {
-                let hir_id = self.cx.tcx.hir.node_to_hir_id(ty.id);
+        if let Some(ref last_path_segment) = last_path_segment(qpath).args {
+            if !last_path_segment.parenthesized
+                && !last_path_segment.args.iter().any(|arg| match arg {
+                    GenericArg::Lifetime(_) => true,
+                    GenericArg::Type(_) => false,
+                })
+            {
+                let hir_id = self.cx.tcx.hir().node_to_hir_id(ty.id);
                 match self.cx.tables.qpath_def(qpath, hir_id) {
                     Def::TyAlias(def_id) | Def::Struct(def_id) => {
                         let generics = self.cx.tcx.generics_of(def_id);
-                        for _ in generics.regions.as_slice() {
+                        for _ in generics.params.as_slice() {
                             self.record(&None);
                         }
                     },
                     Def::Trait(def_id) => {
                         let trait_def = self.cx.tcx.trait_def(def_id);
-                        for _ in &self.cx.tcx.generics_of(trait_def.def_id).regions {
+                        for _ in &self.cx.tcx.generics_of(trait_def.def_id).params {
                             self.record(&None);
                         }
                     },
@@ -321,25 +353,25 @@ fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
 
     fn visit_ty(&mut self, ty: &'tcx Ty) {
         match ty.node {
-            TyRptr(ref lt, _) if lt.is_elided() => {
+            TyKind::Rptr(ref lt, _) if lt.is_elided() => {
                 self.record(&None);
             },
-            TyPath(ref path) => {
+            TyKind::Path(ref path) => {
                 self.collect_anonymous_lifetimes(path, ty);
             },
-            TyImplTraitExistential(ref exist_ty, _) => {
-                for bound in &exist_ty.bounds {
-                    if let RegionTyParamBound(_) = *bound {
-                        self.record(&None);
+            TyKind::Def(item, _) => {
+                if let ItemKind::Existential(ref exist_ty) = self.cx.tcx.hir().expect_item(item.id).node {
+                    for bound in &exist_ty.bounds {
+                        if let GenericBound::Outlives(_) = *bound {
+                            self.record(&None);
+                        }
                     }
+                } else {
+                    unreachable!()
                 }
-            }
-            TyImplTraitUniversal(_, ref param_bounds) => for bound in param_bounds {
-                if let RegionTyParamBound(_) = *bound {
-                    self.record(&None);
-                }
+                walk_ty(self, ty);
             },
-            TyTraitObject(ref bounds, ref lt) => {
+            TyKind::TraitObject(ref bounds, ref lt) => {
                 if !lt.is_elided() {
                     self.abort = true;
                 }
@@ -375,14 +407,16 @@ fn has_where_lifetimes<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, where_clause: &
                 let allowed_lts = allowed_lts_from(&pred.bound_generic_params);
                 // now walk the bounds
                 for bound in pred.bounds.iter() {
-                    walk_ty_param_bound(&mut visitor, bound);
+                    walk_param_bound(&mut visitor, bound);
                 }
                 // and check that all lifetimes are allowed
                 match visitor.into_vec() {
                     None => return false,
-                    Some(lts) => for lt in lts {
-                        if !allowed_lts.contains(&lt) {
-                            return true;
+                    Some(lts) => {
+                        for lt in lts {
+                            if !allowed_lts.contains(&lt) {
+                                return true;
+                            }
                         }
                     },
                 }
@@ -401,13 +435,13 @@ fn has_where_lifetimes<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, where_clause: &
 }
 
 struct LifetimeChecker {
-    map: HashMap<Name, Span>,
+    map: FxHashMap<Name, Span>,
 }
 
 impl<'tcx> Visitor<'tcx> for LifetimeChecker {
     // for lifetimes as parameters of generics
     fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
-        self.map.remove(&lifetime.name.name());
+        self.map.remove(&lifetime.name.ident().name);
     }
 
     fn visit_generic_param(&mut self, param: &'tcx GenericParam) {
@@ -416,7 +450,7 @@ fn visit_generic_param(&mut self, param: &'tcx GenericParam) {
         // don't want to spuriously remove them
         // `'b` in `'a: 'b` is useless unless used elsewhere in
         // a non-lifetime bound
-        if param.is_type_param() {
+        if let GenericParamKind::Type { .. } = param.kind {
             walk_generic_param(self, param)
         }
     }
@@ -427,8 +461,12 @@ fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
 
 fn report_extra_lifetimes<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, func: &'tcx FnDecl, generics: &'tcx Generics) {
     let hs = generics
-        .lifetimes()
-        .map(|lt| (lt.lifetime.name.name(), lt.lifetime.span))
+        .params
+        .iter()
+        .filter_map(|par| match par.kind {
+            GenericParamKind::Lifetime { .. } => Some((par.name.ident().name, par.span)),
+            _ => None,
+        })
         .collect();
     let mut checker = LifetimeChecker { map: hs };
 
@@ -436,7 +474,12 @@ fn report_extra_lifetimes<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, func: &'tcx
     walk_fn_decl(&mut checker, func);
 
     for &v in checker.map.values() {
-        span_lint(cx, UNUSED_LIFETIMES, v, "this lifetime isn't used in the function definition");
+        span_lint(
+            cx,
+            EXTRA_UNUSED_LIFETIMES,
+            v,
+            "this lifetime isn't used in the function definition",
+        );
     }
 }
 
@@ -447,7 +490,7 @@ struct BodyLifetimeChecker {
 impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker {
     // for lifetimes as parameters of generics
     fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
-        if lifetime.name.name() != keywords::Invalid.name() && lifetime.name.name() != "'static" {
+        if lifetime.name.ident().name != keywords::Invalid.name() && lifetime.name.ident().name != "'static" {
             self.lifetimes_used_in_body = true;
         }
     }