Rollup merge of #62453 - zackmdavis:single_path, r=estebank

in which we suggest anonymizing single-use lifetimes in paths

Following @nikomatsakis's [October 2017 comment](https://github.com/rust-lang/rust/issues/44752#issuecomment-340885834).

![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?)
This commit is contained in:
Mazdak Farrokhzad 2019-07-12 22:46:43 +02:00 committed by GitHub
commit e706438d00
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 99 additions and 26 deletions

View file

@ -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;
@ -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) {
@ -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;
}
},
_ => {}
}
}
};
@ -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
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,
);
}
_ => {}
}
}

View file

@ -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() { }

View file

@ -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
|
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