Skip to content

Commit

Permalink
in which we suggest anonymizing single-use lifetimes in paths
Browse files Browse the repository at this point in the history
  • Loading branch information
zackmdavis committed Jul 6, 2019
1 parent e12d682 commit acc4e56
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 27 deletions.
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
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
3 changes: 3 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 @@ -13,7 +13,10 @@ 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() { }
16 changes: 14 additions & 2 deletions src/test/ui/single-use-lifetime/one-use-in-fn-argument.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,30 @@ 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:16:13
--> $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:17:10
--> $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

0 comments on commit acc4e56

Please sign in to comment.