-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
In-band lifetimes: Lint against single-use lifetime names #44752
Comments
Also, I believe, only for lifetime names that appear in argument position. Currently we require explicitly binding output-only lifetimes with a name: fn bar<'a>() -> &'a u8 { &5 } // OK
fn bar() -> &'_ u8 { &5 } // ERROR: missing lifetime specifier |
@cramertj Hmm. I would consider the proper way to declare |
@nikomatsakis Yes, |
Is this up for grabs? |
@gaurikholkar Go for it! |
Will start working on it |
@gaurikholkar hey, just checking in! How's it going? Any blockers? |
Some examples: fn foo<'x>(_: &'x u32) { } This should lint against struct Foo<'a> { x: &'a u32 }
fn foo<'x>(_: Foo<'x>) { } This should lint against struct Foo<'a, 'b> { f: &'a &'b u32 }
fn foo<'x, 'y>(foo: Foo<'x, 'y>) -> &'x u32 { foo.f } This should lint against struct Foo<'a, 'b> { f: &'a &'b u32 }
fn foo<'x, 'y>(foo: Foo<'x, 'y>) -> &'y u32 { foo.f } This should lint against fn foo<'x>() -> &'x u32 { &22 } This should not lint, because trait Trait<'a> { }
impl<'a, T> Trait<'a> for T { }
fn foo<'x, T>(t: T)
where T: Trait<'x>
{
}
fn main() {
foo(22);
} This should not lint, because at present |
OK, let's start with this specific test: fn deref<'x>(v: &'x u32) -> u32 {
*v
}
fn main() { } When we are done, we want to issue a warning, probably like this: fn deref<'x>(v: &'x u32) -> u32 {
// ^^ lifetime name `'x` only used once
*v
}
fn main() { } In this case, the one use of The first thing we want to do then is to figure out how many times each name is used and where. Now, accounting around lifetimes (e.g., early-bound, late-bound, etc) can get kind of complicated, but luckily we can ignore most of that crap for our purposes. I imagine we would want to add to the lifetime_uses: DefIdMap<LifetimeUseSet<'tcx> where a enum LifetimeUseSet<'tcx> {
One(&'tcx hir::Lifetime),
Many,
} We don't really need to track more detail than that -- once there are many uses of a lifetime, we don't want to warn about it anymore, so we don't need to track them. Now, when we are resolving a lifetime in rust/src/librustc/middle/resolve_lifetime.rs Lines 1516 to 1518 in 3cf28f3
Here, we want to match on the match def {
LateBoundAnon(..) | Static => {
// These are anonymous lifetimes or lifetimes that are not declared.
}
Free(_, def_id) | LateBound(_, def_id) | EarlyBound(_, def_id) => {
// A lifetime declared by the user.
if !self.lifetime_uses.contains_key(&def_id) {
self.lifetime_uses.insert(def_id, LifetimeUseSet::One(lifetime_ref));
} else {
self.lifetime_uses.insert(def_id, LifetimeUseSet::Many);
}
}
} Now we have a record of where each lifetime def was used. The next step will be going over the set of lifetime names defined and warning if they are |
I've been in touch with @nikomatsakis over this. The current status is: I've followed the instructions he has given above and written code for the same locally. |
So, if we follow the steps above, we wind up with a map from the def-id of each This is the main function for https://github.com/rust-lang/rust/blob/master/src/librustc/middle/resolve_lifetime.rs#L259-L285 I am imagining that we can basically write a second visitor, https://github.com/rust-lang/rust/blob/master/src/librustc/middle/resolve_lifetime.rs#L287-L290 It would only implement one method, though,
In there, it will check if the map contains OK, this is a not quite right -- we're going to eventually want to be a bit more selective, since it will depend whether that use is one that could be elided or replaced with |
Actually I take that back. Don't use a visitor. Just iterate over the map we built and look things up in the HIR map. For example, given the
|
Well, that won't allow you to catch lifetimes used zero times. To do that, we either need a separate visitor, or visit the map during |
Track the progress here |
Is what gets generated for now |
@nikomatsakis it's compile-fail tests that are failing now |
Lint against single-use lifetime names This is a fix for #44752 TO-DO - [x] change lint message - [x] add ui tests r? @nikomatsakis
I started trying to record all places where this lint might be needed and document their expected behavior. This is not done, but I have to go, so I'm saving my status here for now. My goal is to make a checklist and try to mentor the remaining improvements. |
Here is an updated check-list of the overall goals: moved to issue header |
in which we suggest anonymizing single-use lifetimes in paths Following @nikomatsakis's [October 2017 comment](rust-lang#44752 (comment)). ![path_anon_suggest](https://user-images.githubusercontent.com/1076988/60761598-e2619180-a000-11e9-9144-1bdf8eb848e3.png) r? @estebank cc @eddyb (you were saying something about running single-use-lifetimes against the tree the other week?)
Wow, it's been three years... I still think this is important. I started looking at how - pub fn format(&self, tcx: TyCtxt<'tcx>, mir_body: &'a mir::Body<'tcx>) -> String {
+ pub fn format<'tcx>(&self, tcx: TyCtxt<'tcx>, mir_body: &mir::Body<'tcx>) -> String {
- pub fn format(&self, tcx: TyCtxt<'tcx>, mir_body: &'a mir::Body<'tcx>) -> String {
+ pub fn format<'tcx>(&self, tcx: TyCtxt<'tcx>, mir_body: &mir::Body<'tcx>) -> String {
- pub fn format_coverage_statements(
+ pub fn format_coverage_statements<'tcx>(
&self,
tcx: TyCtxt<'tcx>,
- mir_body: &'a mir::Body<'tcx>,
+ mir_body: &mir::Body<'tcx>,
) -> String {
-pub(super) fn filtered_statement_span(statement: &'a Statement<'tcx>) -> Option<Span> {
+pub(super) fn filtered_statement_span<'tcx>(statement: &Statement<'tcx>) -> Option<Span> {
-pub(super) fn filtered_terminator_span(terminator: &'a Terminator<'tcx>) -> Option<Span> {
+pub(super) fn filtered_terminator_span<'tcx>(terminator: &Terminator<'tcx>) -> Option<Span> { |
Okay, I've created the The examples in the "current status" section at the top all warn and have a suggestion, except for the unused lifetime, which falls under the Marking S-tracking-impl-complete, given that there are several linked issues that need to be fixed. |
With #96833 merged, there has been progress on this issue recently. The number of false positives is now way down, but there's still one remaining, according to the PR's description. |
@est31 what is the remaining false positive? |
Indeed, the remaining false positive is: trait Foo<'a> {}
#[warn(single_use_lifetimes)]
fn foo<'a>(_: impl Foo<'a>) {} Where the lifetime cannot be elided. There is an inconsistency here between plain functions and async functions (which allow elision). I suggested to allow elision in #97720. If that change is accepted, there won't be a false positive any more, and the suggestion will be correct. |
I think the following might be an unaddressed false positive: pub fn get_json<T>(&self, limit: u64) -> Result<(Meta, T)>
where
for<'de> T: Deserialize<'de>,
I'm not aware of any way to use anonymous lifetimes to avoid the single-use lifetime here. |
Another false positive: fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Mime, D::Error> {
Are anonymous lifetimes supposed to work here? The error:
Similar example: fn decode<'i, I>(values: &mut I) -> Result<Self, HeadErr>
where
Self: Sized,
I: Iterator<Item = &'i HeaderValue>, |
@bstrie, those cases are correctly handled by beta 1.63.0 https://play.rust-lang.org/?version=beta&mode=debug&edition=2021&gist=4fd54ae5a4d4a3a936054787f6655b7b |
This appears to ignore the fact that lifetimes inside #![warn(single_use_lifetimes)]
fn test<'a>(val: &(impl 'a + Iterator)) {} This actually suggests removing the lifetime, which in turn is suggested to be added back with: #![warn(single_use_lifetimes)]
fn test(val: &(impl '_ + Iterator)) {} |
rust-lang/rust#44752 (comment) Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com>
rust-lang/rust#44752 (comment) Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com>
rust-lang/rust#44752 (comment) Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com>
rust-lang/rust#44752 (comment) Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com>
rust-lang/rust#44752 (comment) Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com>
rust-lang/rust#44752 (comment) Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com>
rust-lang/rust#44752 (comment) Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com>
* [refactor]: put new API into `iroha_config_base` Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [test]: move tests Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [feat]: create foundation for `ReadConfig` derive (wip) Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [fix]: parse `default` properly Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [feat]: impl shape analysis Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [test]: update stderr Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [fix]: fix macro, it kind of works! Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [refactor]: improve errors Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [refactor]: update `iroha_config` (wip) Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [refactor]: update user layer, mostly Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [refactor]: move `iroha_config` onto the new rails Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [refactor]: lints & chores Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [fix]: fix visibility issues in data model Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [fix]: after-rebase chores Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [refactor]: update `iroha_core` Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [refactor]: update the whole workspace Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [refactor]: lints Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [chore]: fix cfg Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [refactor]: balance trace logs Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [refactor]: move high-level validations to CLI layer Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [refactor]: change `custom` to `env_custom` Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [feat]: attach TOML value to report Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [feat]: enhance origin attachments Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [fix]: client cli... works? Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [refactor]: rehearse errors Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [refactor]: chores Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [fix]: compiler errors and lints Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [test]: fix tests, validate addrs only in `release` Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [refactor]: internal docs, renamings, lints Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [test]: fix private keys in test configs Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [fix]: allow self peer id in trusted peers Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [test]: update snapshot Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: ⭐️NINIKA⭐️ <DCNick3@users.noreply.github.com> Signed-off-by: 0x009922 <43530070+0x009922@users.noreply.github.com> * [feat]: prefix macro error Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [docs]: add a note about syntactic match Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [refactor]: delegate parsing to `syn::punctuated` Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [refactor]: use mutable reader, split `ReadingDone` Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [refactor]: lints Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [chore]: link false-positive issue rust-lang/rust#44752 (comment) Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [refactor]: ignore report locations address #4456 (comment) Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [fix]: use `ExposedPrivateKey` Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [fix]: fix with all-features Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [refactor]: chores Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * [chore]: format Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * refactor: use stricter `TrustedPeers` struct Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * chore: remove extra TODO Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * refactor: remove `env_custom` Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * chore: remove extra import Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * revert: return `pub(crate)` vis Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * chore: dead import Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> * fix: fix path to `iroha_test_config.toml` Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> --------- Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com> Signed-off-by: 0x009922 <43530070+0x009922@users.noreply.github.com> Co-authored-by: ⭐️NINIKA⭐️ <DCNick3@users.noreply.github.com>
Summarizing the current state of this:
I think to make forward progress on this:
|
Once support for
'_
lands in #44691, the next step for #44524 is to implement a lint that warns against "single use" lifetime names.Current status
The lint is partially implemented but needs to be completed. Here is a checklist:
unused_lifetimes
'a
appearing in&'a T
, suggest&T
'a
appearing in any other place, suggest'_
Older Background
The idea is that an explicit name like
'a
should only be used (at least in a function or impl) to link together two things. Otherwise, you should just use'_
to indicate that the lifetime is not linked to anything.Until #15872 is closed, we should only lint for single-use lifetime names that are bound in functions. Once #15872 is closed, we can also lint against those found in impl headers.
We can detect cases where a lint is valid by modifying the
resolve_lifetimes
code:with()
is used to push new scopes on the stack. It is also given a closure which will execute with the new name bindings in scope.with()
gets called for impls and other kinds of items from here; for methods and functions in particular it is called fromvisit_early_late
).resolve_lifetime_ref()
is called to resolve an actual reference to a named lifetime.Scope
, e.g. counting how many times a particular lifetime was referenced.with()
returns, we could scan the lifetimes and check for those that were only referenced 1 time (or 0 times...) and issue a lint warning.(There are some directions for how to add a lint under the header "Issuing future compatibility warnings" in the rustc-bug-fix-procedure page on forge -- we can skip the "future compatibility" parts here.)
The text was updated successfully, but these errors were encountered: