Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new lint for needless lifetimes (fixes #115) #140

Merged
merged 4 commits into from
Aug 13, 2015

Conversation

birkenfeld
Copy link
Contributor

No description provided.

@birkenfeld
Copy link
Contributor Author

Please check my conditions for elision, I'm not 100% firm on these things yet.

@birkenfeld
Copy link
Contributor Author

Note, this will not catch things like

fn test<'a>(&'a self) -> Result<&'a str, Some::Error>

where apparently you can also elide the lifetimes. Is this a good thing? Otherwise I'll have to write a function that walks the types in depth.

// * output reference, exactly one input reference with same LT

// extract lifetime of input arguments
let mut input_lts = func.inputs.iter().filter_map(|arg| extract_lifetime(&*arg.ty))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can just flat_map this with http://manishearth.github.io/rust-internals-docs/rustc/middle/ty/struct.TyS.html#method.walk first to find lifetime params deep in the type sig

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, scratch that, wrong ty.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to write a small Visitor that collects all type params from TyPath and TyRptr, and use http://manishearth.github.io/rust-internals-docs/syntax/visit/fn.walk_ty.html. Not too hard.

@Manishearth
Copy link
Member

The problem is that we might get false positives too if we don't walk the types. For example, the following:

fn test<'a,'b>(&'a self, &'b u8) -> Result<&'a u8, ()>

will lint as elidable, but it isn't.

But it's not too hard to write a visitor that collects lifetimes, so no biggie 😄

Clippy is okay with false positives (they're lints, not compiler errors), but we should minimize them as much as possible.

.collect::<Vec<_>>();
// extract lifetime of "self" for methods
if let FnKind::FkMethod(_, sig, _) = kind {
if let SelfRegion(ref lt_opt, _, _) = sig.explicit_self.node {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can also be SelfExplicit, e.g. fn foo(self: &mut Foo) {}. We should match on SelfExplicit and walk that type too.

@Manishearth
Copy link
Member

Looks like a good start! We just need to extend extract_lifetime to return a vec instead of an option using a visitor, and some other minor edge fixes.

@birkenfeld
Copy link
Contributor Author

Thanks for the review! Looking at the comments now...

@birkenfeld birkenfeld force-pushed the lifetimes branch 2 times, most recently from 6c2c7ca to 3820a05 Compare August 12, 2015 16:25
@birkenfeld
Copy link
Contributor Author

Ok, now we shouldn't have false positives anymore. (Running clippy on a large codebase like rustc might be useful to check?)

I'm mostly concerned about false negatives with the "no output references" case.

// in func.inputs, but its type is TyInfer)
if let FnKind::FkMethod(_, sig, _) = kind {
match sig.explicit_self.node {
SelfRegion(ref opt_lt, _, _) => input_visitor.record(opt_lt),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should additionally walk the inner type, e.g. &'a Foo<'b> and &'a Foo<'a> have different effects, and we should only be suggesting elision for the former

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh nvm I'm an idiot this is a self type. Ignore the above

@Manishearth
Copy link
Member

Yeah, &'static seems to behave like a regular named lifetime for elision. Perhaps we can improve that in the compiler, shouldn't be hard. But we can treat it like a regular lifetime for the hashset stuff.

As far as testing goes, feel free to use it against rustc, but Rust contains a lot of old code so we'll get a lot of legitimate warnings -- we want to look for spurious ones, so perhaps pick something like Hyper and try.

I'll be hooking clippy up to servo at some point, we'll get more feedback then.

@Manishearth
Copy link
Member

LGTM, minor addition to the tests needed.

I'm fine with testing this for false positives after merging -- I'll wait on publishing a new version for a bit then.


#![deny(needless_lifetimes)]

fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) { } //~ERROR
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is good custom to copy the start of the error message after the //~ERROR (if you dislike the width, you can also write //~^ERROR in the next line. This also allows to declare multiple errors in one line, just increment the number of ^). I recently failed to follow this good custom and got a successful compile-fail test when the lint in fact didn't work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally agreed, will use that here, and also in my other recently written tests.

@Manishearth Manishearth merged commit 5952a29 into rust-lang:master Aug 13, 2015
@birkenfeld birkenfeld deleted the lifetimes branch August 13, 2015 07:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants