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

in which we suggest anonymizing single-use lifetimes in paths #62453

Merged
merged 2 commits into from
Jul 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 56 additions & 25 deletions src/librustc/middle/resolve_lifetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::hir::def::{Res, DefKind};
use crate::hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
use crate::hir::map::Map;
use crate::hir::ptr::P;
use crate::hir::{GenericArg, GenericParam, ItemLocalId, LifetimeName, Node, ParamName};
use crate::hir::{GenericArg, GenericParam, ItemLocalId, LifetimeName, Node, ParamName, QPath};
use crate::ty::{self, DefIdTree, GenericParamDefKind, TyCtxt};

use crate::rustc::lint;
Expand Down Expand Up @@ -1458,10 +1458,10 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
}

// helper method to issue suggestions from `fn rah<'a>(&'a T)` to `fn rah(&T)`
// or from `fn rah<'a>(T<'a>)` to `fn rah(T<'_>)`
fn suggest_eliding_single_use_lifetime(
&self, err: &mut DiagnosticBuilder<'_>, def_id: DefId, lifetime: &hir::Lifetime
) {
// FIXME: future work: also suggest `impl Foo<'_>` for `impl<'a> Foo<'a>`
let name = lifetime.name.ident();
let mut remove_decl = None;
if let Some(parent_def_id) = self.tcx.parent(def_id) {
Expand All @@ -1471,18 +1471,38 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
}

let mut remove_use = None;
let mut elide_use = None;
let mut find_arg_use_span = |inputs: &hir::HirVec<hir::Ty>| {
for input in inputs {
if let hir::TyKind::Rptr(lt, _) = input.node {
if lt.name.ident() == name {
// include the trailing whitespace between the ampersand and the type name
let lt_through_ty_span = lifetime.span.to(input.span.shrink_to_hi());
remove_use = Some(
self.tcx.sess.source_map()
.span_until_non_whitespace(lt_through_ty_span)
);
break;
match input.node {
hir::TyKind::Rptr(lt, _) => {
if lt.name.ident() == name {
// include the trailing whitespace between the lifetime and type names
let lt_through_ty_span = lifetime.span.to(input.span.shrink_to_hi());
remove_use = Some(
self.tcx.sess.source_map()
.span_until_non_whitespace(lt_through_ty_span)
);
break;
}
}
hir::TyKind::Path(ref qpath) => {
if let QPath::Resolved(_, path) = qpath {

let last_segment = &path.segments[path.segments.len()-1];
let generics = last_segment.generic_args();
for arg in generics.args.iter() {
if let GenericArg::Lifetime(lt) = arg {
if lt.name.ident() == name {
elide_use = Some(lt.span);
break;
}
}
}
break;
}
},
_ => {}
}
}
};
Expand All @@ -1506,24 +1526,35 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
}
}

if let (Some(decl_span), Some(use_span)) = (remove_decl, remove_use) {
// if both declaration and use deletion spans start at the same
// place ("start at" because the latter includes trailing
// whitespace), then this is an in-band lifetime
if decl_span.shrink_to_lo() == use_span.shrink_to_lo() {
err.span_suggestion(
use_span,
"elide the single-use lifetime",
String::new(),
Applicability::MachineApplicable,
);
} else {
let msg = "elide the single-use lifetime";
match (remove_decl, remove_use, elide_use) {
(Some(decl_span), Some(use_span), None) => {
// if both declaration and use deletion spans start at the same
// place ("start at" because the latter includes trailing
// whitespace), then this is an in-band lifetime
Copy link
Contributor

Choose a reason for hiding this comment

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

This sounds like a bug in the ast's span, right? No need to fix now but add a FIXME

Copy link
Member Author

Choose a reason for hiding this comment

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

No, that's not referring to an AST span; it's referring to a span that we synthesized earlier in the function (the 'a with the trailing space in &'a Foo).

if decl_span.shrink_to_lo() == use_span.shrink_to_lo() {
err.span_suggestion(
use_span,
msg,
String::new(),
Applicability::MachineApplicable,
);
} else {
err.multipart_suggestion(
msg,
vec![(decl_span, String::new()), (use_span, String::new())],
Applicability::MachineApplicable,
);
}
}
(Some(decl_span), None, Some(use_span)) => {
err.multipart_suggestion(
"elide the single-use lifetime",
vec![(decl_span, String::new()), (use_span, String::new())],
msg,
vec![(decl_span, String::new()), (use_span, "'_".to_owned())],
Applicability::MachineApplicable,
);
}
_ => {}
}
}

Expand Down
10 changes: 10 additions & 0 deletions src/test/ui/single-use-lifetime/one-use-in-fn-argument.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,14 @@ fn a<'a>(x: &'a u32) { //~ ERROR `'a` only used once
//~^ HELP elide the single-use lifetime
}

struct Single<'a> { x: &'a u32 }
struct Double<'a, 'b> { f: &'a &'b u32 }

fn center<'m>(_: Single<'m>) {} //~ ERROR `'m` only used once
//~^ HELP elide the single-use lifetime
fn left<'x, 'y>(foo: Double<'x, 'y>) -> &'x u32 { foo.f } //~ ERROR `'y` only used once
//~^ HELP elide the single-use lifetime
fn right<'x, 'y>(foo: Double<'x, 'y>) -> &'y u32 { foo.f } //~ ERROR `'x` only used once
//~^ HELP elide the single-use lifetime

fn main() { }
34 changes: 33 additions & 1 deletion src/test/ui/single-use-lifetime/one-use-in-fn-argument.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,37 @@ help: elide the single-use lifetime
LL | fn a(x: &u32) {
| -- --

error: aborting due to previous error
error: lifetime parameter `'m` only used once
--> $DIR/one-use-in-fn-argument.rs:15:11
|
LL | fn center<'m>(_: Single<'m>) {}
| ^^ -- ...is used only here
| |
| this lifetime...
help: elide the single-use lifetime
|
LL | fn center(_: Single<'_>) {}
| -- ^^

error: lifetime parameter `'y` only used once
--> $DIR/one-use-in-fn-argument.rs:17:13
|
LL | fn left<'x, 'y>(foo: Double<'x, 'y>) -> &'x u32 { foo.f }
| ^^ this lifetime... -- ...is used only here
help: elide the single-use lifetime
Copy link
Member

Choose a reason for hiding this comment

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

Can you perhaps add a rustfix test? I assume this suggestion can be automatically applied, yes?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think it can't due to the multiple ambiguity span bug, unless that has been fixed since Berlin.

|
LL | fn left<'x>(foo: Double<'x, '_>) -> &'x u32 { foo.f }
| -- ^^

error: lifetime parameter `'x` only used once
--> $DIR/one-use-in-fn-argument.rs:19:10
|
LL | fn right<'x, 'y>(foo: Double<'x, 'y>) -> &'y u32 { foo.f }
| ^^ this lifetime... -- ...is used only here
help: elide the single-use lifetime
|
LL | fn right<'y>(foo: Double<'_, 'y>) -> &'y u32 { foo.f }
| -- ^^

error: aborting due to 4 previous errors