lifetimes: try to fix w.r.t. lifetimes from parent scopes (fixes #162)
This commit is contained in:
parent
09db7f3fee
commit
49e51fe65a
2 changed files with 55 additions and 12 deletions
|
@ -18,14 +18,23 @@ impl LintPass for LifetimePass {
|
||||||
lint_array!(NEEDLESS_LIFETIMES)
|
lint_array!(NEEDLESS_LIFETIMES)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_fn(&mut self, cx: &Context, kind: FnKind, decl: &FnDecl,
|
fn check_item(&mut self, cx: &Context, item: &Item) {
|
||||||
_: &Block, span: Span, _: NodeId) {
|
if let ItemFn(ref decl, _, _, _, ref generics, _) = item.node {
|
||||||
if in_external_macro(cx, span) {
|
check_fn_inner(cx, decl, None, &generics.lifetimes, item.span);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
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::*;
|
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:
|
// There are two scenarios where elision works:
|
||||||
// * no output references, all input references have different LT
|
// * no output references, all input references have different LT
|
||||||
// * output references, exactly one input reference with same 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
|
// these will collect all the lifetimes for references in arg/return types
|
||||||
let mut input_visitor = RefVisitor(Vec::new());
|
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
|
// extract lifetime in "self" argument for methods (there is a "self" argument
|
||||||
// in func.inputs, but its type is TyInfer)
|
// in func.inputs, but its type is TyInfer)
|
||||||
if let FnKind::FkMethod(_, sig, _) = kind {
|
if let Some(slf) = slf {
|
||||||
match sig.explicit_self.node {
|
match slf.node {
|
||||||
SelfRegion(ref opt_lt, _, _) => input_visitor.record(opt_lt),
|
SelfRegion(ref opt_lt, _, _) => input_visitor.record(opt_lt),
|
||||||
SelfExplicit(ref ty, _) => walk_ty(&mut input_visitor, ty),
|
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 input_lts = input_visitor.into_vec();
|
||||||
let output_lts = output_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!
|
// no input lifetimes? easy case!
|
||||||
if input_lts.is_empty() {
|
if input_lts.is_empty() {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -31,11 +31,13 @@ fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> { Ok(x) }
|
||||||
|
|
||||||
type Ref<'r> = &'r u8;
|
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
|
//~^ERROR explicit lifetimes given
|
||||||
|
|
||||||
|
fn lifetime_param_3<'a, 'b: 'a>(_x: Ref<'a>, _y: &'b u8) { } // no error, bounded lifetime
|
||||||
|
|
||||||
struct X {
|
struct X {
|
||||||
x: u8,
|
x: u8,
|
||||||
}
|
}
|
||||||
|
@ -68,6 +70,7 @@ fn main() {
|
||||||
let _ = deep_reference_3(&1, 2);
|
let _ = deep_reference_3(&1, 2);
|
||||||
lifetime_param_1(&1, &2);
|
lifetime_param_1(&1, &2);
|
||||||
lifetime_param_2(&1, &2);
|
lifetime_param_2(&1, &2);
|
||||||
|
lifetime_param_3(&1, &2);
|
||||||
|
|
||||||
let foo = X { x: 1 };
|
let foo = X { x: 1 };
|
||||||
foo.self_and_out();
|
foo.self_and_out();
|
||||||
|
|
Loading…
Add table
Reference in a new issue