diff --git a/src/lifetimes.rs b/src/lifetimes.rs index 03b4de6c948..c333f04ab44 100644 --- a/src/lifetimes.rs +++ b/src/lifetimes.rs @@ -18,14 +18,23 @@ impl LintPass for LifetimePass { lint_array!(NEEDLESS_LIFETIMES) } - fn check_fn(&mut self, cx: &Context, kind: FnKind, decl: &FnDecl, - _: &Block, span: Span, _: NodeId) { - if in_external_macro(cx, span) { - return; + 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); } - if could_use_elision(kind, decl) { - span_lint(cx, NEEDLESS_LIFETIMES, span, - "explicit lifetimes given in parameter types where they could be elided"); + } + + 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); + } + } + + 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); } } } @@ -39,10 +48,34 @@ enum RefLt { } use self::RefLt::*; -fn could_use_elision(kind: FnKind, func: &FnDecl) -> bool { +fn check_fn_inner(cx: &Context, decl: &FnDecl, slf: Option<&ExplicitSelf>, + named_lts: &[LifetimeDef], span: Span) { + if in_external_macro(cx, span) { + return; + } + if could_use_elision(decl, slf, named_lts) { + span_lint(cx, NEEDLESS_LIFETIMES, span, + "explicit lifetimes given in parameter types where they could be elided"); + } +} + +fn could_use_elision(func: &FnDecl, slf: Option<&ExplicitSelf>, + named_lts: &[LifetimeDef]) -> bool { // There are two scenarios where elision works: // * no output references, all input references have different LT // * output references, exactly one input reference with same LT + // All lifetimes must be unnamed, 'static or defined without bounds on the + // level of the current item. + + // check named LTs + let mut allowed_lts = HashSet::new(); + for lt in named_lts { + if lt.bounds.is_empty() { + allowed_lts.insert(Named(lt.lifetime.name)); + } + } + allowed_lts.insert(Unnamed); + allowed_lts.insert(Static); // these will collect all the lifetimes for references in arg/return types let mut input_visitor = RefVisitor(Vec::new()); @@ -50,8 +83,8 @@ fn could_use_elision(kind: FnKind, func: &FnDecl) -> bool { // extract lifetime in "self" argument for methods (there is a "self" argument // in func.inputs, but its type is TyInfer) - if let FnKind::FkMethod(_, sig, _) = kind { - match sig.explicit_self.node { + if let Some(slf) = slf { + match slf.node { SelfRegion(ref opt_lt, _, _) => input_visitor.record(opt_lt), SelfExplicit(ref ty, _) => walk_ty(&mut input_visitor, ty), _ => { } @@ -69,6 +102,13 @@ fn could_use_elision(kind: FnKind, func: &FnDecl) -> bool { let input_lts = input_visitor.into_vec(); let output_lts = output_visitor.into_vec(); + // check for lifetimes from higher scopes + for lt in input_lts.iter().chain(output_lts.iter()) { + if !allowed_lts.contains(lt) { + return false; + } + } + // no input lifetimes? easy case! if input_lts.is_empty() { return false; diff --git a/tests/compile-fail/lifetimes.rs b/tests/compile-fail/lifetimes.rs index 36daa69fb31..7f463ec70b4 100755 --- a/tests/compile-fail/lifetimes.rs +++ b/tests/compile-fail/lifetimes.rs @@ -31,11 +31,13 @@ fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> { Ok(x) } type Ref<'r> = &'r u8; -fn lifetime_param_1<'a>(_x: Ref<'a>, _y: &'a u8) { } +fn lifetime_param_1<'a>(_x: Ref<'a>, _y: &'a u8) { } // no error, same lifetime on two params -fn lifetime_param_2<'a, 'b: 'a>(_x: Ref<'a>, _y: &'b u8) { } +fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) { } //~^ERROR explicit lifetimes given +fn lifetime_param_3<'a, 'b: 'a>(_x: Ref<'a>, _y: &'b u8) { } // no error, bounded lifetime + struct X { x: u8, } @@ -68,6 +70,7 @@ fn main() { let _ = deep_reference_3(&1, 2); lifetime_param_1(&1, &2); lifetime_param_2(&1, &2); + lifetime_param_3(&1, &2); let foo = X { x: 1 }; foo.self_and_out();