Skip to content

Commit 46704fd

Browse files
committed
Suggest replacing named lifetime with 'static
``` warning: unnecessary lifetime parameter `'a` --> file.rs:11:14 | 11 | fn foo<'c, 'a: 'static, 'b, 'd>(_x: &'a str) -> &'a str { | ^^^^^^^^^^^ help: you can use the `'static` lifetime directly, in place of `'a` | 11 | fn foo<'c, 'b, 'd>(_x: &'static str) -> &'static str { | -- ^^^^^^^ ^^^^^^^ warning: unnecessary lifetime parameter `'a` --> file.rs:14:9 | 14 | fn bar<'a: 'static>(_x: &'a str) -> &'a str { | ^^^^^^^^^^^ help: you can use the `'static` lifetime directly, in place of `'a` | 14 | fn bar(_x: &'static str) -> &'static str { | -- ^^^^^^^ ^^^^^^^ ```
1 parent ae0659c commit 46704fd

6 files changed

+301
-90
lines changed

src/librustc/hir/mod.rs

+17
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,13 @@ impl GenericParam {
475475
_ => false,
476476
}
477477
}
478+
479+
pub fn span(&self) -> Span {
480+
match *self {
481+
GenericParam::Lifetime(ref lifetime) => lifetime.lifetime.span,
482+
GenericParam::Type(ref typaram) => typaram.span,
483+
}
484+
}
478485
}
479486

480487
pub trait GenericParamsExt {
@@ -1662,6 +1669,16 @@ pub struct Ty {
16621669
pub hir_id: HirId,
16631670
}
16641671

1672+
impl Ty {
1673+
pub fn lifetimes(&self) -> Vec<Lifetime> {
1674+
// FIXME(estebank): expand to all `Ty_`s
1675+
match self.node {
1676+
TyRptr(lifetime, _) => vec![lifetime],
1677+
_ => vec![],
1678+
}
1679+
}
1680+
}
1681+
16651682
impl fmt::Debug for Ty {
16661683
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
16671684
write!(f, "type({})",

src/librustc/middle/resolve_lifetime.rs

+154-78
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
531531
s: ROOT_SCOPE,
532532
};
533533
self.with(scope, |old_scope, this| {
534-
this.check_lifetime_params(old_scope, &generics.params);
534+
this.check_lifetime_params(
535+
old_scope,
536+
&generics.params,
537+
Some(generics.span),
538+
&[],
539+
);
535540
intravisit::walk_item(this, item);
536541
});
537542
}
@@ -574,7 +579,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
574579
self.with(scope, |old_scope, this| {
575580
// a bare fn has no bounds, so everything
576581
// contained within is scoped within its binder.
577-
this.check_lifetime_params(old_scope, &c.generic_params);
582+
this.check_lifetime_params(old_scope, &c.generic_params, None, &[]);
578583
intravisit::walk_ty(this, ty);
579584
});
580585
self.is_in_fn_syntax = was_in_fn_syntax;
@@ -871,7 +876,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
871876
abstract_type_parent: false,
872877
};
873878
let result = self.with(scope, |old_scope, this| {
874-
this.check_lifetime_params(old_scope, &bound_generic_params);
879+
this.check_lifetime_params(old_scope, &bound_generic_params, None, &[]);
875880
this.visit_ty(&bounded_ty);
876881
walk_list!(this, visit_ty_param_bound, bounds);
877882
});
@@ -938,7 +943,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
938943
abstract_type_parent: false,
939944
};
940945
self.with(scope, |old_scope, this| {
941-
this.check_lifetime_params(old_scope, &trait_ref.bound_generic_params);
946+
this.check_lifetime_params(old_scope, &trait_ref.bound_generic_params, None, &[]);
942947
walk_list!(this, visit_generic_param, &trait_ref.bound_generic_params);
943948
this.visit_trait_ref(&trait_ref.trait_ref)
944949
})
@@ -1441,6 +1446,18 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
14411446
.collect();
14421447

14431448
let next_early_index = index + generics.ty_params().count() as u32;
1449+
let mut arg_lifetimes = vec![];
1450+
for input in &decl.inputs {
1451+
for lt in input.lifetimes() {
1452+
arg_lifetimes.push(lt);
1453+
}
1454+
}
1455+
if let hir::FunctionRetTy::Return(ref output) = decl.output {
1456+
for lt in output.lifetimes() {
1457+
arg_lifetimes.push(lt);
1458+
}
1459+
}
1460+
14441461

14451462
let scope = Scope::Binder {
14461463
lifetimes,
@@ -1450,7 +1467,12 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
14501467
track_lifetime_uses: false,
14511468
};
14521469
self.with(scope, move |old_scope, this| {
1453-
this.check_lifetime_params(old_scope, &generics.params);
1470+
this.check_lifetime_params(
1471+
old_scope,
1472+
&generics.params,
1473+
Some(generics.span),
1474+
&arg_lifetimes,
1475+
);
14541476
this.hack(walk); // FIXME(#37666) workaround in place of `walk(this)`
14551477
});
14561478
}
@@ -2031,7 +2053,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
20312053

20322054
if let Some(params) = error {
20332055
if lifetime_refs.len() == 1 {
2034-
self.report_elision_failure(&mut err, params);
2056+
self.report_elision_failure(&mut err, params, span);
20352057
}
20362058
}
20372059

@@ -2042,6 +2064,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
20422064
&mut self,
20432065
db: &mut DiagnosticBuilder,
20442066
params: &[ElisionFailureInfo],
2067+
span: Span,
20452068
) {
20462069
let mut m = String::new();
20472070
let len = params.len();
@@ -2097,18 +2120,22 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
20972120
"this function's return type contains a borrowed value, but \
20982121
there is no value for it to be borrowed from"
20992122
);
2100-
help!(db, "consider giving it a 'static lifetime");
2123+
db.span_suggestion(
2124+
span,
2125+
"consider giving it a `'static` lifetime",
2126+
"&'static ".to_owned(),
2127+
);
21012128
} else if elided_len == 0 {
21022129
help!(
21032130
db,
21042131
"this function's return type contains a borrowed value with \
21052132
an elided lifetime, but the lifetime cannot be derived from \
21062133
the arguments"
21072134
);
2108-
help!(
2109-
db,
2110-
"consider giving it an explicit bounded or 'static \
2111-
lifetime"
2135+
db.span_suggestion(
2136+
span,
2137+
"consider giving it an explicit bound or `'static` lifetime",
2138+
"&'static ".to_owned(),
21122139
);
21132140
} else if elided_len == 1 {
21142141
help!(
@@ -2149,82 +2176,131 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
21492176
self.insert_lifetime(lifetime_ref, lifetime.shifted(late_depth));
21502177
}
21512178

2152-
fn check_lifetime_params(&mut self, old_scope: ScopeRef, params: &'tcx [hir::GenericParam]) {
2153-
for (i, lifetime_i) in params.lifetimes().enumerate() {
2154-
match lifetime_i.lifetime.name {
2155-
hir::LifetimeName::Static | hir::LifetimeName::Underscore => {
2156-
let lifetime = lifetime_i.lifetime;
2157-
let name = lifetime.name.name();
2158-
let mut err = struct_span_err!(
2159-
self.tcx.sess,
2160-
lifetime.span,
2161-
E0262,
2162-
"invalid lifetime parameter name: `{}`",
2163-
name
2164-
);
2165-
err.span_label(
2166-
lifetime.span,
2167-
format!("{} is a reserved lifetime name", name),
2168-
);
2169-
err.emit();
2170-
}
2171-
hir::LifetimeName::Fresh(_)
2172-
| hir::LifetimeName::Implicit
2173-
| hir::LifetimeName::Name(_) => {}
2174-
}
2175-
2176-
// It is a hard error to shadow a lifetime within the same scope.
2177-
for lifetime_j in params.lifetimes().skip(i + 1) {
2178-
if lifetime_i.lifetime.name == lifetime_j.lifetime.name {
2179-
struct_span_err!(
2180-
self.tcx.sess,
2181-
lifetime_j.lifetime.span,
2182-
E0263,
2183-
"lifetime name `{}` declared twice in the same scope",
2184-
lifetime_j.lifetime.name.name()
2185-
).span_label(lifetime_j.lifetime.span, "declared twice")
2186-
.span_label(lifetime_i.lifetime.span, "previous declaration here")
2187-
.emit();
2188-
}
2189-
}
2190-
2191-
// It is a soft error to shadow a lifetime within a parent scope.
2192-
self.check_lifetime_def_for_shadowing(old_scope, &lifetime_i.lifetime);
2193-
2194-
for bound in &lifetime_i.bounds {
2195-
match bound.name {
2196-
hir::LifetimeName::Underscore => {
2179+
fn check_lifetime_params(
2180+
&mut self,
2181+
old_scope: ScopeRef,
2182+
params: &'tcx [hir::GenericParam],
2183+
generics_span: Option<Span>,
2184+
arg_lifetimes: &[hir::Lifetime],
2185+
) {
2186+
for (i, param_i) in params.iter().enumerate() {
2187+
if let hir::GenericParam::Lifetime(lifetime_i) = param_i {
2188+
match lifetime_i.lifetime.name {
2189+
hir::LifetimeName::Static | hir::LifetimeName::Underscore => {
2190+
let lifetime = lifetime_i.lifetime;
2191+
let name = lifetime.name.name();
21972192
let mut err = struct_span_err!(
21982193
self.tcx.sess,
2199-
bound.span,
2200-
E0637,
2201-
"invalid lifetime bound name: `'_`"
2194+
lifetime.span,
2195+
E0262,
2196+
"invalid lifetime parameter name: `{}`",
2197+
name
2198+
);
2199+
err.span_label(
2200+
lifetime.span,
2201+
format!("{} is a reserved lifetime name", name),
22022202
);
2203-
err.span_label(bound.span, "`'_` is a reserved lifetime name");
22042203
err.emit();
22052204
}
2206-
hir::LifetimeName::Static => {
2207-
self.insert_lifetime(bound, Region::Static);
2208-
self.tcx
2209-
.sess
2210-
.struct_span_warn(
2211-
lifetime_i.lifetime.span.to(bound.span),
2205+
hir::LifetimeName::Fresh(_)
2206+
| hir::LifetimeName::Implicit
2207+
| hir::LifetimeName::Name(_) => {}
2208+
}
2209+
2210+
// It is a hard error to shadow a lifetime within the same scope.
2211+
for param_j in params.iter().skip(i + 1) {
2212+
if let hir::GenericParam::Lifetime(lifetime_j) = param_j {
2213+
if lifetime_i.lifetime.name == lifetime_j.lifetime.name {
2214+
struct_span_err!(
2215+
self.tcx.sess,
2216+
lifetime_j.lifetime.span,
2217+
E0263,
2218+
"lifetime name `{}` declared twice in the same scope",
2219+
lifetime_j.lifetime.name.name()
2220+
).span_label(lifetime_j.lifetime.span, "declared twice")
2221+
.span_label(lifetime_i.lifetime.span, "previous declaration here")
2222+
.emit();
2223+
}
2224+
}
2225+
}
2226+
2227+
// It is a soft error to shadow a lifetime within a parent scope.
2228+
self.check_lifetime_def_for_shadowing(old_scope, &lifetime_i.lifetime);
2229+
2230+
for bound in &lifetime_i.bounds {
2231+
match bound.name {
2232+
hir::LifetimeName::Underscore => {
2233+
let mut err = struct_span_err!(
2234+
self.tcx.sess,
2235+
bound.span,
2236+
E0637,
2237+
"invalid lifetime bound name: `'_`"
2238+
);
2239+
err.span_label(bound.span, "`'_` is a reserved lifetime name");
2240+
err.emit();
2241+
}
2242+
hir::LifetimeName::Static => {
2243+
self.insert_lifetime(bound, Region::Static);
2244+
let sp = lifetime_i.lifetime.span.to(bound.span);
2245+
let mut warn = self.tcx.sess.struct_span_warn(
2246+
sp,
22122247
&format!(
22132248
"unnecessary lifetime parameter `{}`",
22142249
lifetime_i.lifetime.name.name()
22152250
),
2216-
)
2217-
.help(&format!(
2218-
"you can use the `'static` lifetime directly, in place \
2219-
of `{}`",
2220-
lifetime_i.lifetime.name.name()
2221-
))
2222-
.emit();
2223-
}
2224-
hir::LifetimeName::Fresh(_)
2225-
| hir::LifetimeName::Implicit
2226-
| hir::LifetimeName::Name(_) => {
2227-
self.resolve_lifetime_ref(bound);
2251+
);
2252+
let mut spans_to_replace = arg_lifetimes.iter().filter_map(|lifetime| {
2253+
if lifetime.name.name() == lifetime_i.lifetime.name.name() {
2254+
Some((lifetime.span, "'static".to_owned()))
2255+
} else {
2256+
None
2257+
}
2258+
}).collect::<Vec<_>>();
2259+
if let (1, Some(sp)) = (params.len(), generics_span) {
2260+
spans_to_replace.push((sp, "".into()));
2261+
warn.multipart_suggestion(
2262+
&format!(
2263+
"you can use the `'static` lifetime directly, \
2264+
in place of `{}`",
2265+
lifetime_i.lifetime.name.name(),
2266+
),
2267+
spans_to_replace,
2268+
);
2269+
} else if params.len() > 1 {
2270+
let sp = if let Some(next_param) = params.iter().nth(i + 1) {
2271+
// we'll remove everything until the next parameter
2272+
lifetime_i.lifetime.span.until(next_param.span())
2273+
} else if let Some(prev_param) = params.iter().nth(i - 1) {
2274+
// this must be the last argument, include the previous comma
2275+
self.tcx.sess.codemap()
2276+
.next_point(prev_param.span())
2277+
.to(sp)
2278+
} else { // THIS SHOULDN'T HAPPEN :|
2279+
sp
2280+
};
2281+
2282+
spans_to_replace.push((sp, "".into()));
2283+
warn.multipart_suggestion(
2284+
&format!(
2285+
"you can use the `'static` lifetime directly, \
2286+
in place of `{}`",
2287+
lifetime_i.lifetime.name.name(),
2288+
),
2289+
spans_to_replace,
2290+
);
2291+
} else {
2292+
warn.help(&format!(
2293+
"you can use the `'static` lifetime directly, in place of `{}`",
2294+
lifetime_i.lifetime.name.name(),
2295+
));
2296+
}
2297+
warn.emit();
2298+
}
2299+
hir::LifetimeName::Fresh(_)
2300+
| hir::LifetimeName::Implicit
2301+
| hir::LifetimeName::Name(_) => {
2302+
self.resolve_lifetime_ref(bound);
2303+
}
22282304
}
22292305
}
22302306
}

src/test/ui/issue-26638.stderr

+8-4
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,23 @@ error[E0106]: missing lifetime specifier
1010
--> $DIR/issue-26638.rs:14:40
1111
|
1212
LL | fn parse_type_2(iter: fn(&u8)->&u8) -> &str { iter() }
13-
| ^ expected lifetime parameter
13+
| ^
14+
| |
15+
| expected lifetime parameter
16+
| help: consider giving it an explicit bound or `'static` lifetime: `&'static`
1417
|
1518
= help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
16-
= help: consider giving it an explicit bounded or 'static lifetime
1719

1820
error[E0106]: missing lifetime specifier
1921
--> $DIR/issue-26638.rs:17:22
2022
|
2123
LL | fn parse_type_3() -> &str { unimplemented!() }
22-
| ^ expected lifetime parameter
24+
| ^
25+
| |
26+
| expected lifetime parameter
27+
| help: consider giving it a `'static` lifetime: `&'static`
2328
|
2429
= help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
25-
= help: consider giving it a 'static lifetime
2630

2731
error: aborting due to 3 previous errors
2832

0 commit comments

Comments
 (0)