X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=clippy_lints%2Fsrc%2Flifetimes.rs;h=573a7c016b8e8c8c6d708fccb3ccd42c0f6f2792;hb=ce847beb4794a5149a714b21b68347daf27a380c;hp=ab5d3fa7b6d9c98b55a5c82c0a192e75f68dde13;hpb=13e8ace73c45a15e07e8e9b14f108b8691bb77b7;p=rust.git diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index ab5d3fa7b6d..573a7c016b8 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -9,12 +9,14 @@ use rustc_hir::FnRetTy::Return; use rustc_hir::{ BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem, - ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, TraitBoundModifier, - TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, + ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, PredicateOrigin, + TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter as middle_nested_filter; +use rustc_middle::ty::TyCtxt; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::def_id::LocalDefId; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, Ident, Symbol}; @@ -36,12 +38,14 @@ /// /// ### Example /// ```rust - /// // Bad: unnecessary lifetime annotations + /// // Unnecessary lifetime annotations /// fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 { /// x /// } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// fn elided(x: &u8, y: u8) -> &u8 { /// x /// } @@ -65,12 +69,14 @@ /// /// ### Example /// ```rust - /// // Bad: unnecessary lifetimes + /// // unnecessary lifetimes /// fn unused_lifetime<'a>(x: u8) { /// // .. /// } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// fn no_lifetime(x: u8) { /// // ... /// } @@ -88,7 +94,9 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Fn(ref sig, generics, id) = item.kind { check_fn_inner(cx, sig.decl, Some(id), None, generics, item.span, true); } else if let ItemKind::Impl(impl_) = item.kind { - report_extra_impl_lifetimes(cx, impl_); + if !item.span.from_expansion() { + report_extra_impl_lifetimes(cx, impl_); + } } } @@ -123,7 +131,7 @@ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_> enum RefLt { Unnamed, Static, - Named(Symbol), + Named(LocalDefId), } fn check_fn_inner<'tcx>( @@ -145,7 +153,7 @@ fn check_fn_inner<'tcx>( .filter(|param| matches!(param.kind, GenericParamKind::Type { .. })); for typ in types { for pred in generics.bounds_for_param(cx.tcx.hir().local_def_id(typ.hir_id)) { - if pred.in_where_clause { + if pred.origin == PredicateOrigin::WhereClause { // has_where_lifetimes checked that this predicate contains no lifetime. continue; } @@ -226,7 +234,7 @@ fn could_use_elision<'tcx>( // level of the current item. // check named LTs - let allowed_lts = allowed_lts_from(named_generics); + let allowed_lts = allowed_lts_from(cx.tcx, named_generics); // these will collect all the lifetimes for references in arg/return types let mut input_visitor = RefVisitor::new(cx); @@ -248,22 +256,6 @@ fn could_use_elision<'tcx>( return false; } - if allowed_lts - .intersection( - &input_visitor - .nested_elision_site_lts - .iter() - .chain(output_visitor.nested_elision_site_lts.iter()) - .cloned() - .filter(|v| matches!(v, RefLt::Named(_))) - .collect(), - ) - .next() - .is_some() - { - return false; - } - let input_lts = input_visitor.lts; let output_lts = output_visitor.lts; @@ -297,6 +289,31 @@ fn could_use_elision<'tcx>( } } + // check for higher-ranked trait bounds + if !input_visitor.nested_elision_site_lts.is_empty() || !output_visitor.nested_elision_site_lts.is_empty() { + let allowed_lts: FxHashSet<_> = allowed_lts + .iter() + .filter_map(|lt| match lt { + RefLt::Named(def_id) => Some(cx.tcx.item_name(def_id.to_def_id())), + _ => None, + }) + .collect(); + for lt in input_visitor.nested_elision_site_lts { + if let RefLt::Named(def_id) = lt { + if allowed_lts.contains(&cx.tcx.item_name(def_id.to_def_id())) { + return false; + } + } + } + for lt in output_visitor.nested_elision_site_lts { + if let RefLt::Named(def_id) = lt { + if allowed_lts.contains(&cx.tcx.item_name(def_id.to_def_id())) { + return false; + } + } + } + } + // no input lifetimes? easy case! if input_lts.is_empty() { false @@ -329,11 +346,11 @@ fn could_use_elision<'tcx>( } } -fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet { +fn allowed_lts_from(tcx: TyCtxt<'_>, named_generics: &[GenericParam<'_>]) -> FxHashSet { let mut allowed_lts = FxHashSet::default(); for par in named_generics.iter() { if let GenericParamKind::Lifetime { .. } = par.kind { - allowed_lts.insert(RefLt::Named(par.name.ident().name)); + allowed_lts.insert(RefLt::Named(tcx.hir().local_def_id(par.hir_id))); } } allowed_lts.insert(RefLt::Unnamed); @@ -371,12 +388,15 @@ fn record(&mut self, lifetime: &Option) { if let Some(ref lt) = *lifetime { if lt.name == LifetimeName::Static { self.lts.push(RefLt::Static); - } else if let LifetimeName::Param(ParamName::Fresh(_)) = lt.name { + } else if let LifetimeName::Param(_, ParamName::Fresh) = lt.name { // Fresh lifetimes generated should be ignored. + self.lts.push(RefLt::Unnamed); } else if lt.is_elided() { self.lts.push(RefLt::Unnamed); + } else if let LifetimeName::Param(def_id, _) = lt.name { + self.lts.push(RefLt::Named(def_id)); } else { - self.lts.push(RefLt::Named(lt.name.ident().name)); + self.lts.push(RefLt::Unnamed); } } else { self.lts.push(RefLt::Unnamed); @@ -424,10 +444,15 @@ fn visit_ty(&mut self, ty: &'tcx Ty<'_>) { TyKind::OpaqueDef(item, bounds) => { let map = self.cx.tcx.hir(); let item = map.item(item); + let len = self.lts.len(); walk_item(self, item); - walk_ty(self, ty); + self.lts.truncate(len); self.lts.extend(bounds.iter().filter_map(|bound| match bound { - GenericArg::Lifetime(l) => Some(RefLt::Named(l.name.ident().name)), + GenericArg::Lifetime(l) => Some(if let LifetimeName::Param(def_id, _) = l.name { + RefLt::Named(def_id) + } else { + RefLt::Unnamed + }), _ => None, })); }, @@ -435,7 +460,6 @@ fn visit_ty(&mut self, ty: &'tcx Ty<'_>) { let mut sub_visitor = RefVisitor::new(self.cx); sub_visitor.visit_fn_decl(decl); self.nested_elision_site_lts.append(&mut sub_visitor.all_lts()); - return; }, TyKind::TraitObject(bounds, ref lt, _) => { if !lt.is_elided() { @@ -444,11 +468,9 @@ fn visit_ty(&mut self, ty: &'tcx Ty<'_>) { for bound in bounds { self.visit_poly_trait_ref(bound, TraitBoundModifier::None); } - return; }, - _ => (), + _ => walk_ty(self, ty), } - walk_ty(self, ty); } } @@ -467,7 +489,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_ return true; } // if the bounds define new lifetimes, they are fine to occur - let allowed_lts = allowed_lts_from(pred.bound_generic_params); + let allowed_lts = allowed_lts_from(cx.tcx, pred.bound_generic_params); // now walk the bounds for bound in pred.bounds.iter() { walk_param_bound(&mut visitor, bound); @@ -591,7 +613,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.ident().name != kw::Empty && lifetime.name.ident().name != kw::StaticLifetime { + if lifetime.name.ident().name != kw::UnderscoreLifetime && lifetime.name.ident().name != kw::StaticLifetime { self.lifetimes_used_in_body = true; } }