From 6461fb3308ee52a04b4856896909fb6974ae4a81 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 30 Aug 2015 09:57:52 +0200 Subject: [PATCH] lifetimes lint: take "where" clauses into account (fixes #253) If a where clause is present and has lifetimes mentioned, just bail out. --- src/lifetimes.rs | 30 ++++++++++++++++++++++++------ tests/compile-fail/lifetimes.rs | 6 ++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/lifetimes.rs b/src/lifetimes.rs index 660d68535bd..bff0db14f7b 100644 --- a/src/lifetimes.rs +++ b/src/lifetimes.rs @@ -20,21 +20,21 @@ impl LintPass for LifetimePass { fn check_item(&mut self, cx: &Context, item: &Item) { if let ItemFn(ref decl, _, _, _, ref generics, _) = item.node { - check_fn_inner(cx, decl, None, &generics.lifetimes, item.span); + check_fn_inner(cx, decl, None, &generics, item.span); } } fn check_impl_item(&mut self, cx: &Context, item: &ImplItem) { if let MethodImplItem(ref sig, _) = item.node { check_fn_inner(cx, &sig.decl, Some(&sig.explicit_self), - &sig.generics.lifetimes, item.span); + &sig.generics, item.span); } } fn check_trait_item(&mut self, cx: &Context, item: &TraitItem) { if let MethodTraitItem(ref sig, _) = item.node { check_fn_inner(cx, &sig.decl, Some(&sig.explicit_self), - &sig.generics.lifetimes, item.span); + &sig.generics, item.span); } } } @@ -49,11 +49,11 @@ enum RefLt { use self::RefLt::*; fn check_fn_inner(cx: &Context, decl: &FnDecl, slf: Option<&ExplicitSelf>, - named_lts: &[LifetimeDef], span: Span) { - if in_external_macro(cx, span) { + generics: &Generics, span: Span) { + if in_external_macro(cx, span) || has_where_lifetimes(&generics.where_clause) { return; } - if could_use_elision(decl, slf, named_lts) { + if could_use_elision(decl, slf, &generics.lifetimes) { span_lint(cx, NEEDLESS_LIFETIMES, span, "explicit lifetimes given in parameter types where they could be elided"); } @@ -182,3 +182,21 @@ impl<'v> Visitor<'v> for RefVisitor { // for lifetime bounds; the default impl calls visit_lifetime_ref fn visit_lifetime_bound(&mut self, _: &'v Lifetime) { } } + +/// Are any lifetimes mentioned in the `where` clause? If yes, we don't try to +/// reason about elision. +fn has_where_lifetimes(where_clause: &WhereClause) -> bool { + let mut where_visitor = RefVisitor(Vec::new()); + for predicate in &where_clause.predicates { + match *predicate { + WherePredicate::RegionPredicate(..) => return true, + WherePredicate::BoundPredicate(ref pred) => { + walk_ty(&mut where_visitor, &pred.bounded_ty); + } + WherePredicate::EqPredicate(ref pred) => { + walk_ty(&mut where_visitor, &pred.ty); + } + } + } + !where_visitor.into_vec().is_empty() +} diff --git a/tests/compile-fail/lifetimes.rs b/tests/compile-fail/lifetimes.rs index a5597e6478f..ae115efec04 100755 --- a/tests/compile-fail/lifetimes.rs +++ b/tests/compile-fail/lifetimes.rs @@ -31,6 +31,10 @@ fn deep_reference_2<'a>(x: Result<&'a u8, &'a u8>) -> &'a u8 { x.unwrap() } // n fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> { Ok(x) } //~^ERROR explicit lifetimes given +// where clause, but without lifetimes +fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> where T: Copy { Ok(x) } +//~^ERROR explicit lifetimes given + type Ref<'r> = &'r u8; fn lifetime_param_1<'a>(_x: Ref<'a>, _y: &'a u8) { } // no error, same lifetime on two params @@ -40,6 +44,8 @@ fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) { } fn lifetime_param_3<'a, 'b: 'a>(_x: Ref<'a>, _y: &'b u8) { } // no error, bounded lifetime +fn lifetime_param_4<'a, 'b>(_x: Ref<'a>, _y: &'b u8) where 'b: 'a { } // no error, bounded lifetime + struct X { x: u8, }