From e12d682dde0adbbde3d49dd202b223deb1ceec89 Mon Sep 17 00:00:00 2001 From: "Zack M. Davis" Date: Sat, 6 Jul 2019 13:20:35 -0700 Subject: [PATCH 1/2] path-type examples for single-use lifetime in fn argument UI test Niko Matsakis commented in October 2017 (https://github.com/rust-lang/rust/issues/44752#issuecomment-340885834) that these should lint. They do! Let's reify that in the tests now (and then we'll see a nice diff when we add suggestions in a future commit). --- .../one-use-in-fn-argument.rs | 7 ++++++ .../one-use-in-fn-argument.stderr | 22 ++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/test/ui/single-use-lifetime/one-use-in-fn-argument.rs b/src/test/ui/single-use-lifetime/one-use-in-fn-argument.rs index 900e8434d4ff5..60435c4ad2436 100644 --- a/src/test/ui/single-use-lifetime/one-use-in-fn-argument.rs +++ b/src/test/ui/single-use-lifetime/one-use-in-fn-argument.rs @@ -9,4 +9,11 @@ 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 +fn left<'x, 'y>(foo: Double<'x, 'y>) -> &'x u32 { foo.f } //~ ERROR `'y` only used once +fn right<'x, 'y>(foo: Double<'x, 'y>) -> &'y u32 { foo.f } //~ ERROR `'x` only used once + fn main() { } diff --git a/src/test/ui/single-use-lifetime/one-use-in-fn-argument.stderr b/src/test/ui/single-use-lifetime/one-use-in-fn-argument.stderr index 4bf08534b8c48..e8513e6647a5e 100644 --- a/src/test/ui/single-use-lifetime/one-use-in-fn-argument.stderr +++ b/src/test/ui/single-use-lifetime/one-use-in-fn-argument.stderr @@ -16,5 +16,25 @@ 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... + +error: lifetime parameter `'y` only used once + --> $DIR/one-use-in-fn-argument.rs:16:13 + | +LL | fn left<'x, 'y>(foo: Double<'x, 'y>) -> &'x u32 { foo.f } + | ^^ this lifetime... -- ...is used only here + +error: lifetime parameter `'x` only used once + --> $DIR/one-use-in-fn-argument.rs:17:10 + | +LL | fn right<'x, 'y>(foo: Double<'x, 'y>) -> &'y u32 { foo.f } + | ^^ this lifetime... -- ...is used only here + +error: aborting due to 4 previous errors From acc4e564feb5151ca02070e0890e428d7967b8b4 Mon Sep 17 00:00:00 2001 From: "Zack M. Davis" Date: Sat, 6 Jul 2019 15:02:27 -0700 Subject: [PATCH 2/2] in which we suggest anonymizing single-use lifetimes in paths --- src/librustc/middle/resolve_lifetime.rs | 81 +++++++++++++------ .../one-use-in-fn-argument.rs | 3 + .../one-use-in-fn-argument.stderr | 16 +++- 3 files changed, 73 insertions(+), 27 deletions(-) diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 3221b41ee1d44..beb8061842dd3 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -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| { 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, ); } + _ => {} } } diff --git a/src/test/ui/single-use-lifetime/one-use-in-fn-argument.rs b/src/test/ui/single-use-lifetime/one-use-in-fn-argument.rs index 60435c4ad2436..ff9d6bd01c670 100644 --- a/src/test/ui/single-use-lifetime/one-use-in-fn-argument.rs +++ b/src/test/ui/single-use-lifetime/one-use-in-fn-argument.rs @@ -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() { } diff --git a/src/test/ui/single-use-lifetime/one-use-in-fn-argument.stderr b/src/test/ui/single-use-lifetime/one-use-in-fn-argument.stderr index e8513e6647a5e..faaa7e2f1b01b 100644 --- a/src/test/ui/single-use-lifetime/one-use-in-fn-argument.stderr +++ b/src/test/ui/single-use-lifetime/one-use-in-fn-argument.stderr @@ -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