Skip to content

Commit 86b88e6

Browse files
committed
Auto merge of #55432 - zackmdavis:single_life, r=nikomatsakis
single life * structured ~~autofixable~~ (well, pending #53934 and rust-lang/rustfix#141) suggestions for the single-use-lifetimes lint in the case of function and method reference args * don't consider the anonymous lifetime `'_` as "single-use" (it's intended for exactly this sort of thing) ![single_life](https://user-images.githubusercontent.com/1076988/47613227-3b2b6400-da48-11e8-8efd-cb975ddf537d.png) r? @nikomatsakis
2 parents 794fc06 + faea5bb commit 86b88e6

16 files changed

+181
-41
lines changed

src/librustc/middle/resolve_lifetime.rs

+107-17
Original file line numberDiff line numberDiff line change
@@ -1454,23 +1454,101 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
14541454
/// helper method to determine the span to remove when suggesting the
14551455
/// deletion of a lifetime
14561456
fn lifetime_deletion_span(&self, name: ast::Ident, generics: &hir::Generics) -> Option<Span> {
1457-
if generics.params.len() == 1 {
1458-
// if sole lifetime, remove the `<>` brackets
1459-
Some(generics.span)
1460-
} else {
1461-
generics.params.iter().enumerate().find_map(|(i, param)| {
1462-
if param.name.ident() == name {
1463-
// We also want to delete a leading or trailing comma
1464-
// as appropriate
1465-
if i >= generics.params.len() - 1 {
1466-
Some(generics.params[i - 1].span.shrink_to_hi().to(param.span))
1467-
} else {
1468-
Some(param.span.to(generics.params[i + 1].span.shrink_to_lo()))
1457+
generics.params.iter().enumerate().find_map(|(i, param)| {
1458+
if param.name.ident() == name {
1459+
let mut in_band = false;
1460+
if let hir::GenericParamKind::Lifetime { kind } = param.kind {
1461+
if let hir::LifetimeParamKind::InBand = kind {
1462+
in_band = true;
14691463
}
1464+
}
1465+
if in_band {
1466+
Some(param.span)
14701467
} else {
1471-
None
1468+
if generics.params.len() == 1 {
1469+
// if sole lifetime, remove the entire `<>` brackets
1470+
Some(generics.span)
1471+
} else {
1472+
// if removing within `<>` brackets, we also want to
1473+
// delete a leading or trailing comma as appropriate
1474+
if i >= generics.params.len() - 1 {
1475+
Some(generics.params[i - 1].span.shrink_to_hi().to(param.span))
1476+
} else {
1477+
Some(param.span.to(generics.params[i + 1].span.shrink_to_lo()))
1478+
}
1479+
}
14721480
}
1473-
})
1481+
} else {
1482+
None
1483+
}
1484+
})
1485+
}
1486+
1487+
// helper method to issue suggestions from `fn rah<'a>(&'a T)` to `fn rah(&T)`
1488+
fn suggest_eliding_single_use_lifetime(
1489+
&self, err: &mut DiagnosticBuilder<'_>, def_id: DefId, lifetime: &hir::Lifetime
1490+
) {
1491+
// FIXME: future work: also suggest `impl Foo<'_>` for `impl<'a> Foo<'a>`
1492+
let name = lifetime.name.ident();
1493+
let mut remove_decl = None;
1494+
if let Some(parent_def_id) = self.tcx.parent(def_id) {
1495+
if let Some(generics) = self.tcx.hir.get_generics(parent_def_id) {
1496+
remove_decl = self.lifetime_deletion_span(name, generics);
1497+
}
1498+
}
1499+
1500+
let mut remove_use = None;
1501+
let mut find_arg_use_span = |inputs: &hir::HirVec<hir::Ty>| {
1502+
for input in inputs {
1503+
if let hir::TyKind::Rptr(lt, _) = input.node {
1504+
if lt.name.ident() == name {
1505+
// include the trailing whitespace between the ampersand and the type name
1506+
let lt_through_ty_span = lifetime.span.to(input.span.shrink_to_hi());
1507+
remove_use = Some(
1508+
self.tcx.sess.source_map()
1509+
.span_until_non_whitespace(lt_through_ty_span)
1510+
);
1511+
break;
1512+
}
1513+
}
1514+
}
1515+
};
1516+
if let Node::Lifetime(hir_lifetime) = self.tcx.hir.get(lifetime.id) {
1517+
if let Some(parent) = self.tcx.hir.find(self.tcx.hir.get_parent(hir_lifetime.id)) {
1518+
match parent {
1519+
Node::Item(item) => {
1520+
if let hir::ItemKind::Fn(decl, _, _, _) = &item.node {
1521+
find_arg_use_span(&decl.inputs);
1522+
}
1523+
},
1524+
Node::ImplItem(impl_item) => {
1525+
if let hir::ImplItemKind::Method(sig, _) = &impl_item.node {
1526+
find_arg_use_span(&sig.decl.inputs);
1527+
}
1528+
}
1529+
_ => {}
1530+
}
1531+
}
1532+
}
1533+
1534+
if let (Some(decl_span), Some(use_span)) = (remove_decl, remove_use) {
1535+
// if both declaration and use deletion spans start at the same
1536+
// place ("start at" because the latter includes trailing
1537+
// whitespace), then this is an in-band lifetime
1538+
if decl_span.shrink_to_lo() == use_span.shrink_to_lo() {
1539+
err.span_suggestion_with_applicability(
1540+
use_span,
1541+
"elide the single-use lifetime",
1542+
String::new(),
1543+
Applicability::MachineApplicable,
1544+
);
1545+
} else {
1546+
err.multipart_suggestion_with_applicability(
1547+
"elide the single-use lifetime",
1548+
vec![(decl_span, String::new()), (use_span, String::new())],
1549+
Applicability::MachineApplicable,
1550+
);
1551+
}
14741552
}
14751553
}
14761554

@@ -1526,14 +1604,26 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
15261604
_ => None,
15271605
} {
15281606
debug!("id = {:?} span = {:?} name = {:?}", node_id, span, name);
1607+
1608+
if name == keywords::UnderscoreLifetime.ident() {
1609+
continue;
1610+
}
1611+
15291612
let mut err = self.tcx.struct_span_lint_node(
15301613
lint::builtin::SINGLE_USE_LIFETIMES,
15311614
id,
15321615
span,
15331616
&format!("lifetime parameter `{}` only used once", name),
15341617
);
1535-
err.span_label(span, "this lifetime...");
1536-
err.span_label(lifetime.span, "...is used only here");
1618+
1619+
if span == lifetime.span {
1620+
// spans are the same for in-band lifetime declarations
1621+
err.span_label(span, "this lifetime is only used here");
1622+
} else {
1623+
err.span_label(span, "this lifetime...");
1624+
err.span_label(lifetime.span, "...is used only here");
1625+
}
1626+
self.suggest_eliding_single_use_lifetime(&mut err, def_id, lifetime);
15371627
err.emit();
15381628
}
15391629
}
@@ -1566,7 +1656,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
15661656
if let Some(span) = unused_lt_span {
15671657
err.span_suggestion_with_applicability(
15681658
span,
1569-
"remove it",
1659+
"elide the unused lifetime",
15701660
String::new(),
15711661
Applicability::MachineApplicable,
15721662
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// run-rustfix
12+
13+
#![feature(in_band_lifetimes)]
14+
#![deny(single_use_lifetimes)]
15+
#![allow(dead_code)]
16+
#![allow(unused_variables)]
17+
18+
// Test that we DO warn when lifetime name is used only
19+
// once in a fn argument, even with in band lifetimes.
20+
21+
fn a(x: &u32, y: &u32) {
22+
//~^ ERROR `'a` only used once
23+
//~| ERROR `'b` only used once
24+
//~| HELP elide the single-use lifetime
25+
//~| HELP elide the single-use lifetime
26+
}
27+
28+
fn main() { }

src/test/ui/single-use-lifetime/one-use-in-fn-argument-in-band.rs

+4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
// run-rustfix
12+
1113
#![feature(in_band_lifetimes)]
1214
#![deny(single_use_lifetimes)]
1315
#![allow(dead_code)]
@@ -19,6 +21,8 @@
1921
fn a(x: &'a u32, y: &'b u32) {
2022
//~^ ERROR `'a` only used once
2123
//~| ERROR `'b` only used once
24+
//~| HELP elide the single-use lifetime
25+
//~| HELP elide the single-use lifetime
2226
}
2327

2428
fn main() { }
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
11
error: lifetime parameter `'a` only used once
2-
--> $DIR/one-use-in-fn-argument-in-band.rs:19:10
2+
--> $DIR/one-use-in-fn-argument-in-band.rs:21:10
33
|
44
LL | fn a(x: &'a u32, y: &'b u32) {
5-
| ^^
5+
| ^^-
66
| |
7-
| this lifetime...
8-
| ...is used only here
7+
| this lifetime is only used here
8+
| help: elide the single-use lifetime
99
|
1010
note: lint level defined here
11-
--> $DIR/one-use-in-fn-argument-in-band.rs:12:9
11+
--> $DIR/one-use-in-fn-argument-in-band.rs:14:9
1212
|
1313
LL | #![deny(single_use_lifetimes)]
1414
| ^^^^^^^^^^^^^^^^^^^^
1515

1616
error: lifetime parameter `'b` only used once
17-
--> $DIR/one-use-in-fn-argument-in-band.rs:19:22
17+
--> $DIR/one-use-in-fn-argument-in-band.rs:21:22
1818
|
1919
LL | fn a(x: &'a u32, y: &'b u32) {
20-
| ^^
20+
| ^^-
2121
| |
22-
| this lifetime...
23-
| ...is used only here
22+
| this lifetime is only used here
23+
| help: elide the single-use lifetime
2424

2525
error: aborting due to 2 previous errors
2626

src/test/ui/single-use-lifetime/one-use-in-fn-argument.rs

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
// once in a fn argument.
1717

1818
fn a<'a>(x: &'a u32) { //~ ERROR `'a` only used once
19+
//~^ HELP elide the single-use lifetime
1920
}
2021

2122
fn main() { }

src/test/ui/single-use-lifetime/one-use-in-fn-argument.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ note: lint level defined here
1111
|
1212
LL | #![deny(single_use_lifetimes)]
1313
| ^^^^^^^^^^^^^^^^^^^^
14+
help: elide the single-use lifetime
15+
|
16+
LL | fn a(x: &u32) { //~ ERROR `'a` only used once
17+
| -- --
1418

1519
error: aborting due to previous error
1620

src/test/ui/single-use-lifetime/one-use-in-inherent-impl-header.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,8 @@
1212
#![allow(dead_code)]
1313
#![allow(unused_variables)]
1414

15-
// Test that we DO warn for a lifetime used only once in an impl.
16-
//
17-
// (Actually, until #15872 is fixed, you can't use `'_` here, but
18-
// hopefully that will come soon.)
15+
// Test that we DO warn for a lifetime used only once in an impl, and that we
16+
// don't warn for the anonymous lifetime.
1917

2018
struct Foo<'f> {
2119
data: &'f u32
@@ -26,4 +24,9 @@ impl<'f> Foo<'f> { //~ ERROR `'f` only used once
2624
}
2725
}
2826

27+
impl Foo<'_> {
28+
fn inherent_b(&self) {}
29+
}
30+
31+
2932
fn main() { }

src/test/ui/single-use-lifetime/one-use-in-inherent-impl-header.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: lifetime parameter `'f` only used once
2-
--> $DIR/one-use-in-inherent-impl-header.rs:24:6
2+
--> $DIR/one-use-in-inherent-impl-header.rs:22:6
33
|
44
LL | impl<'f> Foo<'f> { //~ ERROR `'f` only used once
55
| ^^ -- ...is used only here

src/test/ui/single-use-lifetime/one-use-in-inherent-method-argument.rs

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ struct Foo<'f> {
2020

2121
impl<'f> Foo<'f> { //~ ERROR `'f` only used once
2222
fn inherent_a<'a>(&self, data: &'a u32) { //~ ERROR `'a` only used once
23+
//~^ HELP elide the single-use lifetime
2324
}
2425
}
2526

src/test/ui/single-use-lifetime/one-use-in-inherent-method-argument.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ note: lint level defined here
1111
|
1212
LL | #![deny(single_use_lifetimes)]
1313
| ^^^^^^^^^^^^^^^^^^^^
14+
help: elide the single-use lifetime
15+
|
16+
LL | fn inherent_a(&self, data: &u32) { //~ ERROR `'a` only used once
17+
| -- --
1418

1519
error: lifetime parameter `'f` only used once
1620
--> $DIR/one-use-in-inherent-method-argument.rs:21:6

src/test/ui/single-use-lifetime/one-use-in-trait-method-argument.rs

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ impl<'f> Iterator for Foo<'f> {
2323
type Item = &'f u32;
2424

2525
fn next<'g>(&'g mut self) -> Option<Self::Item> { //~ ERROR `'g` only used once
26+
//~^ HELP elide the single-use lifetime
2627
None
2728
}
2829
}

src/test/ui/single-use-lifetime/one-use-in-trait-method-argument.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ note: lint level defined here
1111
|
1212
LL | #![deny(single_use_lifetimes)]
1313
| ^^^^^^^^^^^^^^^^^^^^
14+
help: elide the single-use lifetime
15+
|
16+
LL | fn next(&mut self) -> Option<Self::Item> { //~ ERROR `'g` only used once
17+
| ----
1418

1519
error: aborting due to previous error
1620

src/test/ui/single-use-lifetime/zero-uses-in-fn.fixed

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,17 @@
77

88
fn september() {}
99
//~^ ERROR lifetime parameter `'a` never used
10-
//~| HELP remove it
10+
//~| HELP elide the unused lifetime
1111

1212
fn october<'b, T>(s: &'b T) -> &'b T {
1313
//~^ ERROR lifetime parameter `'a` never used
14-
//~| HELP remove it
14+
//~| HELP elide the unused lifetime
1515
s
1616
}
1717

1818
fn november<'a>(s: &'a str) -> (&'a str) {
1919
//~^ ERROR lifetime parameter `'b` never used
20-
//~| HELP remove it
20+
//~| HELP elide the unused lifetime
2121
s
2222
}
2323

src/test/ui/single-use-lifetime/zero-uses-in-fn.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,17 @@
77

88
fn september<'a>() {}
99
//~^ ERROR lifetime parameter `'a` never used
10-
//~| HELP remove it
10+
//~| HELP elide the unused lifetime
1111

1212
fn october<'a, 'b, T>(s: &'b T) -> &'b T {
1313
//~^ ERROR lifetime parameter `'a` never used
14-
//~| HELP remove it
14+
//~| HELP elide the unused lifetime
1515
s
1616
}
1717

1818
fn november<'a, 'b>(s: &'a str) -> (&'a str) {
1919
//~^ ERROR lifetime parameter `'b` never used
20-
//~| HELP remove it
20+
//~| HELP elide the unused lifetime
2121
s
2222
}
2323

src/test/ui/single-use-lifetime/zero-uses-in-fn.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error: lifetime parameter `'a` never used
22
--> $DIR/zero-uses-in-fn.rs:8:14
33
|
44
LL | fn september<'a>() {}
5-
| -^^- help: remove it
5+
| -^^- help: elide the unused lifetime
66
|
77
note: lint level defined here
88
--> $DIR/zero-uses-in-fn.rs:5:9
@@ -16,15 +16,15 @@ error: lifetime parameter `'a` never used
1616
LL | fn october<'a, 'b, T>(s: &'b T) -> &'b T {
1717
| ^^--
1818
| |
19-
| help: remove it
19+
| help: elide the unused lifetime
2020

2121
error: lifetime parameter `'b` never used
2222
--> $DIR/zero-uses-in-fn.rs:18:17
2323
|
2424
LL | fn november<'a, 'b>(s: &'a str) -> (&'a str) {
2525
| --^^
2626
| |
27-
| help: remove it
27+
| help: elide the unused lifetime
2828

2929
error: aborting due to 3 previous errors
3030

0 commit comments

Comments
 (0)