diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 361abb1689619..31e34895052a3 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -1443,23 +1443,101 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { /// helper method to determine the span to remove when suggesting the /// deletion of a lifetime fn lifetime_deletion_span(&self, name: ast::Ident, generics: &hir::Generics) -> Option<Span> { - if generics.params.len() == 1 { - // if sole lifetime, remove the `<>` brackets - Some(generics.span) - } else { - generics.params.iter().enumerate().find_map(|(i, param)| { - if param.name.ident() == name { - // We also want to delete a leading or trailing comma - // as appropriate - if i >= generics.params.len() - 1 { - Some(generics.params[i - 1].span.shrink_to_hi().to(param.span)) - } else { - Some(param.span.to(generics.params[i + 1].span.shrink_to_lo())) + generics.params.iter().enumerate().find_map(|(i, param)| { + if param.name.ident() == name { + let mut in_band = false; + if let hir::GenericParamKind::Lifetime { kind } = param.kind { + if let hir::LifetimeParamKind::InBand = kind { + in_band = true; } + } + if in_band { + Some(param.span) } else { - None + if generics.params.len() == 1 { + // if sole lifetime, remove the entire `<>` brackets + Some(generics.span) + } else { + // if removing within `<>` brackets, we also want to + // delete a leading or trailing comma as appropriate + if i >= generics.params.len() - 1 { + Some(generics.params[i - 1].span.shrink_to_hi().to(param.span)) + } else { + Some(param.span.to(generics.params[i + 1].span.shrink_to_lo())) + } + } } - }) + } else { + None + } + }) + } + + // helper method to issue suggestions from `fn rah<'a>(&'a T)` 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) { + if let Some(generics) = self.tcx.hir.get_generics(parent_def_id) { + remove_decl = self.lifetime_deletion_span(name, generics); + } + } + + let mut remove_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; + } + } + } + }; + if let Node::Lifetime(hir_lifetime) = self.tcx.hir.get(lifetime.id) { + if let Some(parent) = self.tcx.hir.find(self.tcx.hir.get_parent(hir_lifetime.id)) { + match parent { + Node::Item(item) => { + if let hir::ItemKind::Fn(decl, _, _, _) = &item.node { + find_arg_use_span(&decl.inputs); + } + }, + Node::ImplItem(impl_item) => { + if let hir::ImplItemKind::Method(sig, _) = &impl_item.node { + find_arg_use_span(&sig.decl.inputs); + } + } + _ => {} + } + } + } + + 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_with_applicability( + use_span, + "elide the single-use lifetime", + String::new(), + Applicability::MachineApplicable, + ); + } else { + err.multipart_suggestion_with_applicability( + "elide the single-use lifetime", + vec![(decl_span, String::new()), (use_span, String::new())], + Applicability::MachineApplicable, + ); + } } } @@ -1515,14 +1593,26 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { _ => None, } { debug!("id = {:?} span = {:?} name = {:?}", node_id, span, name); + + if name == keywords::UnderscoreLifetime.ident() { + continue; + } + let mut err = self.tcx.struct_span_lint_node( lint::builtin::SINGLE_USE_LIFETIMES, id, span, &format!("lifetime parameter `{}` only used once", name), ); - err.span_label(span, "this lifetime..."); - err.span_label(lifetime.span, "...is used only here"); + + if span == lifetime.span { + // spans are the same for in-band lifetime declarations + err.span_label(span, "this lifetime is only used here"); + } else { + err.span_label(span, "this lifetime..."); + err.span_label(lifetime.span, "...is used only here"); + } + self.suggest_eliding_single_use_lifetime(&mut err, def_id, lifetime); err.emit(); } } @@ -1555,7 +1645,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { if let Some(span) = unused_lt_span { err.span_suggestion_with_applicability( span, - "remove it", + "elide the unused lifetime", String::new(), Applicability::MachineApplicable, ); diff --git a/src/test/ui/single-use-lifetime/one-use-in-fn-argument-in-band.fixed b/src/test/ui/single-use-lifetime/one-use-in-fn-argument-in-band.fixed new file mode 100644 index 0000000000000..fd9b10e1a32ea --- /dev/null +++ b/src/test/ui/single-use-lifetime/one-use-in-fn-argument-in-band.fixed @@ -0,0 +1,28 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// run-rustfix + +#![feature(in_band_lifetimes)] +#![deny(single_use_lifetimes)] +#![allow(dead_code)] +#![allow(unused_variables)] + +// Test that we DO warn when lifetime name is used only +// once in a fn argument, even with in band lifetimes. + +fn a(x: &u32, y: &u32) { + //~^ ERROR `'a` only used once + //~| ERROR `'b` only used once + //~| HELP elide the single-use lifetime + //~| HELP elide the single-use lifetime +} + +fn main() { } diff --git a/src/test/ui/single-use-lifetime/one-use-in-fn-argument-in-band.rs b/src/test/ui/single-use-lifetime/one-use-in-fn-argument-in-band.rs index 1aad3265cbe0a..0d5f65e6d1b53 100644 --- a/src/test/ui/single-use-lifetime/one-use-in-fn-argument-in-band.rs +++ b/src/test/ui/single-use-lifetime/one-use-in-fn-argument-in-band.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// run-rustfix + #![feature(in_band_lifetimes)] #![deny(single_use_lifetimes)] #![allow(dead_code)] @@ -19,6 +21,8 @@ fn a(x: &'a u32, y: &'b u32) { //~^ ERROR `'a` only used once //~| ERROR `'b` only used once + //~| HELP elide the single-use lifetime + //~| HELP elide the single-use lifetime } fn main() { } diff --git a/src/test/ui/single-use-lifetime/one-use-in-fn-argument-in-band.stderr b/src/test/ui/single-use-lifetime/one-use-in-fn-argument-in-band.stderr index 6d18075ba37cf..a9f8411d41103 100644 --- a/src/test/ui/single-use-lifetime/one-use-in-fn-argument-in-band.stderr +++ b/src/test/ui/single-use-lifetime/one-use-in-fn-argument-in-band.stderr @@ -1,26 +1,26 @@ error: lifetime parameter `'a` only used once - --> $DIR/one-use-in-fn-argument-in-band.rs:19:10 + --> $DIR/one-use-in-fn-argument-in-band.rs:21:10 | LL | fn a(x: &'a u32, y: &'b u32) { - | ^^ + | ^^- | | - | this lifetime... - | ...is used only here + | this lifetime is only used here + | help: elide the single-use lifetime | note: lint level defined here - --> $DIR/one-use-in-fn-argument-in-band.rs:12:9 + --> $DIR/one-use-in-fn-argument-in-band.rs:14:9 | LL | #![deny(single_use_lifetimes)] | ^^^^^^^^^^^^^^^^^^^^ error: lifetime parameter `'b` only used once - --> $DIR/one-use-in-fn-argument-in-band.rs:19:22 + --> $DIR/one-use-in-fn-argument-in-band.rs:21:22 | LL | fn a(x: &'a u32, y: &'b u32) { - | ^^ + | ^^- | | - | this lifetime... - | ...is used only here + | this lifetime is only used here + | help: elide the single-use lifetime error: aborting due to 2 previous errors 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 9ac813a24ded3..a1b8866c8c455 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 @@ -16,6 +16,7 @@ // once in a fn argument. fn a<'a>(x: &'a u32) { //~ ERROR `'a` 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 4c13133581bb4..0a23ecaf458ad 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 @@ -11,6 +11,10 @@ note: lint level defined here | LL | #![deny(single_use_lifetimes)] | ^^^^^^^^^^^^^^^^^^^^ +help: elide the single-use lifetime + | +LL | fn a(x: &u32) { //~ ERROR `'a` only used once + | -- -- error: aborting due to previous error diff --git a/src/test/ui/single-use-lifetime/one-use-in-inherent-impl-header.rs b/src/test/ui/single-use-lifetime/one-use-in-inherent-impl-header.rs index a862bbbe30c09..b392f7a51746f 100644 --- a/src/test/ui/single-use-lifetime/one-use-in-inherent-impl-header.rs +++ b/src/test/ui/single-use-lifetime/one-use-in-inherent-impl-header.rs @@ -12,10 +12,8 @@ #![allow(dead_code)] #![allow(unused_variables)] -// Test that we DO warn for a lifetime used only once in an impl. -// -// (Actually, until #15872 is fixed, you can't use `'_` here, but -// hopefully that will come soon.) +// Test that we DO warn for a lifetime used only once in an impl, and that we +// don't warn for the anonymous lifetime. struct Foo<'f> { data: &'f u32 @@ -26,4 +24,9 @@ impl<'f> Foo<'f> { //~ ERROR `'f` only used once } } +impl Foo<'_> { + fn inherent_b(&self) {} +} + + fn main() { } diff --git a/src/test/ui/single-use-lifetime/one-use-in-inherent-impl-header.stderr b/src/test/ui/single-use-lifetime/one-use-in-inherent-impl-header.stderr index 2509366f9696f..40dfa677d0ab2 100644 --- a/src/test/ui/single-use-lifetime/one-use-in-inherent-impl-header.stderr +++ b/src/test/ui/single-use-lifetime/one-use-in-inherent-impl-header.stderr @@ -1,5 +1,5 @@ error: lifetime parameter `'f` only used once - --> $DIR/one-use-in-inherent-impl-header.rs:24:6 + --> $DIR/one-use-in-inherent-impl-header.rs:22:6 | LL | impl<'f> Foo<'f> { //~ ERROR `'f` only used once | ^^ -- ...is used only here diff --git a/src/test/ui/single-use-lifetime/one-use-in-inherent-method-argument.rs b/src/test/ui/single-use-lifetime/one-use-in-inherent-method-argument.rs index 863d8faef6ea1..63c3bbdfe4a11 100644 --- a/src/test/ui/single-use-lifetime/one-use-in-inherent-method-argument.rs +++ b/src/test/ui/single-use-lifetime/one-use-in-inherent-method-argument.rs @@ -20,6 +20,7 @@ struct Foo<'f> { impl<'f> Foo<'f> { //~ ERROR `'f` only used once fn inherent_a<'a>(&self, data: &'a u32) { //~ ERROR `'a` only used once + //~^ HELP elide the single-use lifetime } } diff --git a/src/test/ui/single-use-lifetime/one-use-in-inherent-method-argument.stderr b/src/test/ui/single-use-lifetime/one-use-in-inherent-method-argument.stderr index cfc8dbf18dc01..d0651a437b091 100644 --- a/src/test/ui/single-use-lifetime/one-use-in-inherent-method-argument.stderr +++ b/src/test/ui/single-use-lifetime/one-use-in-inherent-method-argument.stderr @@ -11,6 +11,10 @@ note: lint level defined here | LL | #![deny(single_use_lifetimes)] | ^^^^^^^^^^^^^^^^^^^^ +help: elide the single-use lifetime + | +LL | fn inherent_a(&self, data: &u32) { //~ ERROR `'a` only used once + | -- -- error: lifetime parameter `'f` only used once --> $DIR/one-use-in-inherent-method-argument.rs:21:6 diff --git a/src/test/ui/single-use-lifetime/one-use-in-trait-method-argument.rs b/src/test/ui/single-use-lifetime/one-use-in-trait-method-argument.rs index 389ba5c873ad8..c36e9ef2b2237 100644 --- a/src/test/ui/single-use-lifetime/one-use-in-trait-method-argument.rs +++ b/src/test/ui/single-use-lifetime/one-use-in-trait-method-argument.rs @@ -23,6 +23,7 @@ impl<'f> Iterator for Foo<'f> { type Item = &'f u32; fn next<'g>(&'g mut self) -> Option<Self::Item> { //~ ERROR `'g` only used once + //~^ HELP elide the single-use lifetime None } } diff --git a/src/test/ui/single-use-lifetime/one-use-in-trait-method-argument.stderr b/src/test/ui/single-use-lifetime/one-use-in-trait-method-argument.stderr index 4a796d83242fd..e054d0ad9349b 100644 --- a/src/test/ui/single-use-lifetime/one-use-in-trait-method-argument.stderr +++ b/src/test/ui/single-use-lifetime/one-use-in-trait-method-argument.stderr @@ -11,6 +11,10 @@ note: lint level defined here | LL | #![deny(single_use_lifetimes)] | ^^^^^^^^^^^^^^^^^^^^ +help: elide the single-use lifetime + | +LL | fn next(&mut self) -> Option<Self::Item> { //~ ERROR `'g` only used once + | ---- error: aborting due to previous error diff --git a/src/test/ui/single-use-lifetime/zero-uses-in-fn.fixed b/src/test/ui/single-use-lifetime/zero-uses-in-fn.fixed index 5ba7df8a1e61f..89607af260a52 100644 --- a/src/test/ui/single-use-lifetime/zero-uses-in-fn.fixed +++ b/src/test/ui/single-use-lifetime/zero-uses-in-fn.fixed @@ -7,17 +7,17 @@ fn september() {} //~^ ERROR lifetime parameter `'a` never used -//~| HELP remove it +//~| HELP elide the unused lifetime fn october<'b, T>(s: &'b T) -> &'b T { //~^ ERROR lifetime parameter `'a` never used - //~| HELP remove it + //~| HELP elide the unused lifetime s } fn november<'a>(s: &'a str) -> (&'a str) { //~^ ERROR lifetime parameter `'b` never used - //~| HELP remove it + //~| HELP elide the unused lifetime s } diff --git a/src/test/ui/single-use-lifetime/zero-uses-in-fn.rs b/src/test/ui/single-use-lifetime/zero-uses-in-fn.rs index a56d7fa8abc02..be0bdb9b62851 100644 --- a/src/test/ui/single-use-lifetime/zero-uses-in-fn.rs +++ b/src/test/ui/single-use-lifetime/zero-uses-in-fn.rs @@ -7,17 +7,17 @@ fn september<'a>() {} //~^ ERROR lifetime parameter `'a` never used -//~| HELP remove it +//~| HELP elide the unused lifetime fn october<'a, 'b, T>(s: &'b T) -> &'b T { //~^ ERROR lifetime parameter `'a` never used - //~| HELP remove it + //~| HELP elide the unused lifetime s } fn november<'a, 'b>(s: &'a str) -> (&'a str) { //~^ ERROR lifetime parameter `'b` never used - //~| HELP remove it + //~| HELP elide the unused lifetime s } diff --git a/src/test/ui/single-use-lifetime/zero-uses-in-fn.stderr b/src/test/ui/single-use-lifetime/zero-uses-in-fn.stderr index 566c841cfa969..2ccba796d4229 100644 --- a/src/test/ui/single-use-lifetime/zero-uses-in-fn.stderr +++ b/src/test/ui/single-use-lifetime/zero-uses-in-fn.stderr @@ -2,7 +2,7 @@ error: lifetime parameter `'a` never used --> $DIR/zero-uses-in-fn.rs:8:14 | LL | fn september<'a>() {} - | -^^- help: remove it + | -^^- help: elide the unused lifetime | note: lint level defined here --> $DIR/zero-uses-in-fn.rs:5:9 @@ -16,7 +16,7 @@ error: lifetime parameter `'a` never used LL | fn october<'a, 'b, T>(s: &'b T) -> &'b T { | ^^-- | | - | help: remove it + | help: elide the unused lifetime error: lifetime parameter `'b` never used --> $DIR/zero-uses-in-fn.rs:18:17 @@ -24,7 +24,7 @@ error: lifetime parameter `'b` never used LL | fn november<'a, 'b>(s: &'a str) -> (&'a str) { | --^^ | | - | help: remove it + | help: elide the unused lifetime error: aborting due to 3 previous errors diff --git a/src/test/ui/single-use-lifetime/zero-uses-in-impl.stderr b/src/test/ui/single-use-lifetime/zero-uses-in-impl.stderr index 1b77ebdec99c6..3f9d907ade679 100644 --- a/src/test/ui/single-use-lifetime/zero-uses-in-impl.stderr +++ b/src/test/ui/single-use-lifetime/zero-uses-in-impl.stderr @@ -2,7 +2,7 @@ error: lifetime parameter `'a` never used --> $DIR/zero-uses-in-impl.rs:8:6 | LL | impl<'a> Foo {} //~ ERROR `'a` never used - | -^^- help: remove it + | -^^- help: elide the unused lifetime | note: lint level defined here --> $DIR/zero-uses-in-impl.rs:3:9