X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=clippy_lints%2Fsrc%2Flifetimes.rs;h=32170c9a7c6a7ce036a794631cc7a406ae77c419;hb=4d60841205d10293d6759df1948acda4c607c7eb;hp=b0fd62089ba4a3ee18d9675443d261aa63f8e860;hpb=d9a80d2f84458dda53d31f627b27aa9b9b2bc39b;p=rust.git diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index b0fd62089ba..32170c9a7c6 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -1,11 +1,22 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + use crate::reexport::*; -use rustc::lint::*; +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 crate::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 @@ -20,13 +31,15 @@ /// /// **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_clippy_lint! { - pub NEEDLESS_LIFETIMES, - complexity, - "using explicit lifetimes for references in function arguments when elision rules \ - would allow omitting them" +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 @@ -40,7 +53,9 @@ /// /// **Example:** /// ```rust -/// fn unused_lifetime<'a>(x: u8) { .. } +/// fn unused_lifetime<'a>(x: u8) { +/// .. +/// } /// ``` declare_clippy_lint! { pub EXTRA_UNUSED_LIFETIMES, @@ -59,7 +74,7 @@ fn get_lints(&self) -> LintArray { 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,46 +111,52 @@ 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(); - generics.params.iter().for_each(|param| match param.kind { - GenericParamKind::Lifetime { .. } => {}, - GenericParamKind::Type { .. } => { - for bound in ¶m.bounds { - 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") - .args; - if let Some(ref params) = *params { - for bound in ¶ms.lifetimes { - if bound.name.name() != "'static" && !bound.is_elided() { - return; - } - bounds_lts.push(bound); + 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 { + 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") + .args; + if let Some(ref params) = *params { + 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); } } } - }, - }); + } + } if could_use_elision(cx, decl, body, &generics.params, bounds_lts) { span_lint( 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); @@ -183,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; } @@ -203,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; } @@ -230,12 +249,12 @@ fn could_use_elision<'a, 'tcx: 'a>( } } -fn allowed_lts_from(named_generics: &[GenericParam]) -> HashSet { - let mut allowed_lts = HashSet::new(); +fn allowed_lts_from(named_generics: &[GenericParam]) -> FxHashSet { + 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)); } } } @@ -246,8 +265,8 @@ fn allowed_lts_from(named_generics: &[GenericParam]) -> HashSet { fn lts_from_bounds<'a, T: Iterator>(mut vec: Vec, bounds_lts: T) -> Vec { 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)); } } @@ -256,7 +275,7 @@ fn lts_from_bounds<'a, T: Iterator>(mut vec: Vec, bo /// Number of unique lifetimes in the given vector. fn unique_lifetimes(lts: &[RefLt]) -> usize { - lts.iter().collect::>().len() + lts.iter().collect::>().len() } /// A visitor usable for `rustc_front::visit::walk_ty()`. @@ -277,12 +296,12 @@ fn new(cx: &'v LateContext<'v, 't>) -> Self { fn record(&mut self, lifetime: &Option) { 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); @@ -299,8 +318,13 @@ fn into_vec(self) -> Option> { fn collect_anonymous_lifetimes(&mut self, qpath: &QPath, ty: &Ty) { if let Some(ref last_path_segment) = last_path_segment(qpath).args { - 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 !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); @@ -329,22 +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(exist_ty_id, _, _) => { - if let ItemExistential(ref exist_ty) = self.cx.tcx.hir.expect_item(exist_ty_id.id).node { + 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!() } - } - TyTraitObject(ref bounds, ref lt) => { + walk_ty(self, ty); + }, + TyKind::TraitObject(ref bounds, ref lt) => { if !lt.is_elided() { self.abort = true; } @@ -385,9 +412,11 @@ fn has_where_lifetimes<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, where_clause: & // and check that all lifetimes are allowed match visitor.into_vec() { None => return false, - Some(lts) => for lt in lts { - if !allowed_lts.contains(<) { - return true; + Some(lts) => { + for lt in lts { + if !allowed_lts.contains(<) { + return true; + } } }, } @@ -406,13 +435,13 @@ fn has_where_lifetimes<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, where_clause: & } struct LifetimeChecker { - map: HashMap, + map: FxHashMap, } 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) { @@ -432,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 }; @@ -441,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, EXTRA_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", + ); } } @@ -452,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; } }