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

single life #55432

Merged
merged 2 commits into from
Nov 4, 2018
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
124 changes: 107 additions & 17 deletions src/librustc/middle/resolve_lifetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
);
}
}
}

Expand Down Expand Up @@ -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();
}
}
Expand Down Expand Up @@ -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,
);
Expand Down
Original file line number Diff line number Diff line change
@@ -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() { }
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -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() { }
Original file line number Diff line number Diff line change
@@ -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

1 change: 1 addition & 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 @@ -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() { }
4 changes: 4 additions & 0 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 @@ -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

Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -26,4 +24,9 @@ impl<'f> Foo<'f> { //~ ERROR `'f` only used once
}
}

impl Foo<'_> {
fn inherent_b(&self) {}
}


fn main() { }
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

6 changes: 3 additions & 3 deletions src/test/ui/single-use-lifetime/zero-uses-in-fn.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
6 changes: 3 additions & 3 deletions src/test/ui/single-use-lifetime/zero-uses-in-fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
6 changes: 3 additions & 3 deletions src/test/ui/single-use-lifetime/zero-uses-in-fn.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -16,15 +16,15 @@ 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
|
LL | fn november<'a, 'b>(s: &'a str) -> (&'a str) {
| --^^
| |
| help: remove it
| help: elide the unused 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...looks funny, no? It seems like we'd be better off making this suggestion "out of line"?


error: aborting due to 3 previous errors

Loading