Skip to content

Commit 734db3e

Browse files
committed
structured suggestions for elided-lifetimes-in-paths lint
This was a doozy! We only issue a suggestion when there's just one implicit lifetime, because there can be situations where there are several (notice how `elided_path_lifetimes` in librustc/hir/lowering.rs takes a number of expected lifetimes as an argument), but we don't have an actionable suggestion to offer (e.g., if there are two implicit lifetimes in a return type, we hit E0106 "missing lifetime specifiers" as well as (still) triggering this lint). This has to do with rust-lang#52041.
1 parent f841067 commit 734db3e

File tree

4 files changed

+163
-26
lines changed

4 files changed

+163
-26
lines changed

src/librustc/middle/resolve_lifetime.rs

Lines changed: 77 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use hir::map::Map;
2121
use hir::{GenericArg, GenericParam, ItemLocalId, LifetimeName, ParamName};
2222
use ty::{self, TyCtxt, GenericParamDefKind};
2323

24-
use errors::DiagnosticBuilder;
24+
use errors::{Applicability, DiagnosticBuilder};
2525
use rustc::lint;
2626
use rustc_data_structures::sync::Lrc;
2727
use session::Session;
@@ -609,7 +609,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
609609
// resolved the same as the `'_` in `&'_ Foo`.
610610
//
611611
// cc #48468
612-
self.resolve_elided_lifetimes(vec![lifetime], false)
612+
self.resolve_elided_lifetimes(vec![lifetime])
613613
}
614614
LifetimeName::Param(_) | LifetimeName::Static => {
615615
// If the user wrote an explicit name, use that.
@@ -855,7 +855,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
855855

856856
fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
857857
if lifetime_ref.is_elided() {
858-
self.resolve_elided_lifetimes(vec![lifetime_ref], false);
858+
self.resolve_elided_lifetimes(vec![lifetime_ref]);
859859
return;
860860
}
861861
if lifetime_ref.is_static() {
@@ -866,10 +866,10 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
866866
}
867867

868868
fn visit_path(&mut self, path: &'tcx hir::Path, _: ast::NodeId) {
869-
for (i, segment) in path.segments.iter().enumerate() {
869+
for (i, ref segment) in path.segments.iter().enumerate() {
870870
let depth = path.segments.len() - i - 1;
871871
if let Some(ref args) = segment.args {
872-
self.visit_segment_args(path.def, depth, args);
872+
self.visit_segment_args(path.def, depth, segment.ident, args);
873873
}
874874
}
875875
}
@@ -1667,6 +1667,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
16671667
&mut self,
16681668
def: Def,
16691669
depth: usize,
1670+
segment_ident: ast::Ident,
16701671
generic_args: &'tcx hir::GenericArgs,
16711672
) {
16721673
if generic_args.parenthesized {
@@ -1687,9 +1688,12 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
16871688
Some(lt)
16881689
}
16891690
_ => None,
1690-
}).collect();
1691+
}).collect::<Vec<_>>();
16911692
if elide_lifetimes {
1692-
self.resolve_elided_lifetimes(lifetimes, true);
1693+
self.lint_implicit_lifetimes_in_segment(
1694+
segment_ident, generic_args, &lifetimes
1695+
);
1696+
self.resolve_elided_lifetimes(lifetimes);
16931697
} else {
16941698
lifetimes.iter().for_each(|lt| self.visit_lifetime(lt));
16951699
}
@@ -2066,27 +2070,80 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
20662070
}
20672071
}
20682072

2073+
fn lint_implicit_lifetimes_in_segment(&mut self,
2074+
segment_ident: ast::Ident,
2075+
generic_args: &'tcx hir::GenericArgs,
2076+
lifetime_refs: &[&'tcx hir::Lifetime]) {
2077+
let num_implicit_lifetimes = lifetime_refs.iter()
2078+
.filter(|lt| lt.name.is_implicit()).count();
2079+
if num_implicit_lifetimes == 0 {
2080+
return;
2081+
}
2082+
2083+
let mut err = self.tcx.struct_span_lint_node(
2084+
lint::builtin::ELIDED_LIFETIMES_IN_PATHS,
2085+
lifetime_refs[0].id, // FIXME: HirIdify #50928
2086+
segment_ident.span,
2087+
&format!("implicit lifetime parameters in types are deprecated"),
2088+
);
2089+
2090+
if num_implicit_lifetimes == 1 {
2091+
let (replace_span,
2092+
suggestion) = if generic_args.args.len() == num_implicit_lifetimes &&
2093+
generic_args.bindings.is_empty() {
2094+
// If there are no (non-implicit) generic args or bindings, our
2095+
// suggestion includes the angle brackets
2096+
(segment_ident.span.shrink_to_hi(), "<'_>")
2097+
} else {
2098+
// Otherwise—sorry, this is kind of gross—we need to infer the
2099+
// replacement point span from the generics that do exist
2100+
let mut first_generic_span = None;
2101+
for ref arg in &generic_args.args {
2102+
match arg {
2103+
hir::GenericArg::Lifetime(lt) => {
2104+
// Really, this branch shouldn't happen—you can't elide only
2105+
// some lifetimes—but the responsibility for catching that
2106+
// error lives elsewhere
2107+
if !lt.name.is_implicit() {
2108+
first_generic_span = Some(lt.span);
2109+
break;
2110+
}
2111+
},
2112+
hir::GenericArg::Type(ty) => {
2113+
first_generic_span = Some(ty.span);
2114+
break;
2115+
}
2116+
}
2117+
}
2118+
if let None = first_generic_span {
2119+
for ref binding in &generic_args.bindings {
2120+
first_generic_span = Some(binding.span);
2121+
break;
2122+
}
2123+
}
2124+
let replace_span = first_generic_span
2125+
.expect("checked earlier that non-implicit args or bindings exist");
2126+
(replace_span.shrink_to_lo(), "'_, ")
2127+
};
2128+
err.span_suggestion_with_applicability(
2129+
replace_span,
2130+
"indicate the anonymous lifetime",
2131+
suggestion.to_owned(),
2132+
Applicability::MachineApplicable
2133+
);
2134+
}
2135+
err.emit();
2136+
}
2137+
20692138
fn resolve_elided_lifetimes(&mut self,
2070-
lifetime_refs: Vec<&'tcx hir::Lifetime>,
2071-
deprecate_implicit: bool) {
2139+
lifetime_refs: Vec<&'tcx hir::Lifetime>) {
20722140
if lifetime_refs.is_empty() {
20732141
return;
20742142
}
20752143

20762144
let span = lifetime_refs[0].span;
20772145
let mut late_depth = 0;
20782146
let mut scope = self.scope;
2079-
if deprecate_implicit && lifetime_refs[0].name.is_implicit() {
2080-
let mut err = self.tcx.struct_span_lint_node(
2081-
lint::builtin::ELIDED_LIFETIMES_IN_PATHS,
2082-
lifetime_refs[0].id, // FIXME: HirIdify #50928
2083-
span,
2084-
&format!("implicit lifetime parameters in types are deprecated"),
2085-
);
2086-
// FIXME: suggest `'_` (need to take into account whether angle-bracketed
2087-
// params already exist)
2088-
err.emit();
2089-
}
20902147
let error = loop {
20912148
match *scope {
20922149
// Do not assign any resolution, it will be inferred.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright 2018 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+
// compile-flags: --edition 2018
13+
14+
#![allow(unused)]
15+
#![deny(elided_lifetimes_in_paths)]
16+
//~^ NOTE lint level defined here
17+
18+
use std::cell::{RefCell, Ref};
19+
20+
21+
struct Foo<'a> { x: &'a u32 }
22+
23+
fn foo(x: &Foo<'_>) {
24+
//~^ ERROR implicit lifetime parameters in types are deprecated
25+
//~| HELP indicate the anonymous lifetime
26+
}
27+
28+
fn bar(x: &Foo<'_>) {}
29+
30+
31+
struct Wrapped<'a>(&'a str);
32+
33+
fn wrap_gift(gift: &str) -> Wrapped<'_> {
34+
//~^ ERROR implicit lifetime parameters in types are deprecated
35+
//~| HELP indicate the anonymous lifetime
36+
Wrapped(gift)
37+
}
38+
39+
40+
fn main() {
41+
let honesty = RefCell::new((4, 'e'));
42+
let loyalty: Ref<'_, (u32, char)> = honesty.borrow();
43+
//~^ ERROR implicit lifetime parameters in types are deprecated
44+
//~| HELP indicate the anonymous lifetime
45+
}

src/test/ui/in-band-lifetimes/elided-lifetimes.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,38 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
// run-rustfix
12+
// compile-flags: --edition 2018
13+
1114
#![allow(unused)]
1215
#![deny(elided_lifetimes_in_paths)]
16+
//~^ NOTE lint level defined here
17+
18+
use std::cell::{RefCell, Ref};
19+
1320

1421
struct Foo<'a> { x: &'a u32 }
1522

1623
fn foo(x: &Foo) {
17-
//~^ ERROR: implicit lifetime parameters in types are deprecated
24+
//~^ ERROR implicit lifetime parameters in types are deprecated
25+
//~| HELP indicate the anonymous lifetime
1826
}
1927

2028
fn bar(x: &Foo<'_>) {}
2129

22-
fn main() {}
30+
31+
struct Wrapped<'a>(&'a str);
32+
33+
fn wrap_gift(gift: &str) -> Wrapped {
34+
//~^ ERROR implicit lifetime parameters in types are deprecated
35+
//~| HELP indicate the anonymous lifetime
36+
Wrapped(gift)
37+
}
38+
39+
40+
fn main() {
41+
let honesty = RefCell::new((4, 'e'));
42+
let loyalty: Ref<(u32, char)> = honesty.borrow();
43+
//~^ ERROR implicit lifetime parameters in types are deprecated
44+
//~| HELP indicate the anonymous lifetime
45+
}
Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,26 @@
11
error: implicit lifetime parameters in types are deprecated
2-
--> $DIR/elided-lifetimes.rs:16:12
2+
--> $DIR/elided-lifetimes.rs:23:12
33
|
44
LL | fn foo(x: &Foo) {
5-
| ^^^
5+
| ^^^- help: indicate the anonymous lifetime: `<'_>`
66
|
77
note: lint level defined here
8-
--> $DIR/elided-lifetimes.rs:12:9
8+
--> $DIR/elided-lifetimes.rs:15:9
99
|
1010
LL | #![deny(elided_lifetimes_in_paths)]
1111
| ^^^^^^^^^^^^^^^^^^^^^^^^^
1212

13-
error: aborting due to previous error
13+
error: implicit lifetime parameters in types are deprecated
14+
--> $DIR/elided-lifetimes.rs:33:29
15+
|
16+
LL | fn wrap_gift(gift: &str) -> Wrapped {
17+
| ^^^^^^^- help: indicate the anonymous lifetime: `<'_>`
18+
19+
error: implicit lifetime parameters in types are deprecated
20+
--> $DIR/elided-lifetimes.rs:42:18
21+
|
22+
LL | let loyalty: Ref<(u32, char)> = honesty.borrow();
23+
| ^^^ - help: indicate the anonymous lifetime: `'_,`
24+
25+
error: aborting due to 3 previous errors
1426

0 commit comments

Comments
 (0)