diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index b7c66398f8500..7b2e62914ae3d 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -475,6 +475,13 @@ impl GenericParam { _ => false, } } + + pub fn span(&self) -> Span { + match *self { + GenericParam::Lifetime(ref lifetime) => lifetime.lifetime.span, + GenericParam::Type(ref typaram) => typaram.span, + } + } } pub trait GenericParamsExt { @@ -1662,6 +1669,137 @@ pub struct Ty { pub hir_id: HirId, } +impl Ty { + pub fn lifetimes(&self) -> Vec { + // FIXME(estebank): expand to all `Ty_`s + match self.node { + TyRptr(lifetime, ref mut_ty) => { + let mut lifetimes = vec![lifetime]; + lifetimes.extend(mut_ty.ty.lifetimes()); + lifetimes + } + TyPtr(ref mut_ty) => { + let mut lifetimes = vec![]; + lifetimes.extend(mut_ty.ty.lifetimes()); + lifetimes + } + TySlice(ref ty) | + TyArray(ref ty, _) => ty.lifetimes(), + TyBareFn(ref bare_fn) => { + let mut lifetimes = vec![]; + for ty in &bare_fn.decl.inputs { + lifetimes.extend(ty.lifetimes()); + } + if let FunctionRetTy::Return(ref ty) = bare_fn.decl.output { + lifetimes.extend(ty.lifetimes()); + } + lifetimes + } + TyPath(ref path) => { + match *path { + QPath::Resolved(ref ty, ref path) => { + let mut lifetimes = vec![]; + if let &Some(ref ty) = ty { + lifetimes.extend(ty.lifetimes()); + } + for segment in &path.segments { + if let Some(ref params) = segment.parameters { + for lifetime in ¶ms.lifetimes { + lifetimes.push(*lifetime); + } + for ty in ¶ms.types { + lifetimes.extend(ty.lifetimes()); + } + } + } + lifetimes + } + QPath::TypeRelative(ref ty, ref path_segment) => { + let mut lifetimes = vec![]; + lifetimes.extend(ty.lifetimes()); + if let Some(ref params) = path_segment.parameters { + for lifetime in ¶ms.lifetimes { + lifetimes.push(*lifetime); + } + for ty in ¶ms.types { + lifetimes.extend(ty.lifetimes()); + } + } + lifetimes + } + } + } + TyTup(ref tys) => { + let mut lifetimes = vec![]; + for ty in tys { + lifetimes.extend(ty.lifetimes()); + } + lifetimes + } + TyTraitObject(ref poly_trait_refs, lifetime) => { + let mut lifetimes = vec![lifetime]; + for poly_trait_ref in poly_trait_refs { + for param in &poly_trait_ref.bound_generic_params { + if let GenericParam::Lifetime(lifetime_def) = param { + lifetimes.push(lifetime_def.lifetime); + } + } + for segment in &poly_trait_ref.trait_ref.path.segments { + if let Some(ref params) = segment.parameters { + for lifetime in ¶ms.lifetimes { + lifetimes.push(*lifetime); + } + for ty in ¶ms.types { + lifetimes.extend(ty.lifetimes()); + } + } + } + } + lifetimes + } + TyImplTraitExistential(ref exist_ty, ref ex_lifetimes) => { + let mut lifetimes = vec![]; + lifetimes.extend(ex_lifetimes); + for param in &exist_ty.generics.params { + if let GenericParam::Lifetime(lifetime_def) = param { + lifetimes.push(lifetime_def.lifetime); + } + } + for param in &exist_ty.generics.params { + if let GenericParam::Lifetime(lifetime_def) = param { + lifetimes.push(lifetime_def.lifetime); + } + } + for bound in &exist_ty.bounds { + match bound { + TyParamBound::RegionTyParamBound(lifetime) => lifetimes.push(*lifetime), + + TyParamBound::TraitTyParamBound(ref poly_trait_ref, _) => { + for param in &poly_trait_ref.bound_generic_params { + if let GenericParam::Lifetime(lifetime_def) = param { + lifetimes.push(lifetime_def.lifetime); + } + for segment in &poly_trait_ref.trait_ref.path.segments { + if let Some(ref params) = segment.parameters { + for lifetime in ¶ms.lifetimes { + lifetimes.push(*lifetime); + } + for ty in ¶ms.types { + lifetimes.extend(ty.lifetimes()); + } + } + } + } + } + } + } + lifetimes + } + TyNever | TyErr | TyInfer | TyTypeof(_) => vec![], + } + } +} + impl fmt::Debug for Ty { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "type({})", diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 14c1993e28e15..73edef5960a16 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -531,7 +531,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { s: ROOT_SCOPE, }; self.with(scope, |old_scope, this| { - this.check_lifetime_params(old_scope, &generics.params); + this.check_lifetime_params( + old_scope, + &generics.params, + Some(generics.span), + &[], + ); intravisit::walk_item(this, item); }); } @@ -574,7 +579,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { self.with(scope, |old_scope, this| { // a bare fn has no bounds, so everything // contained within is scoped within its binder. - this.check_lifetime_params(old_scope, &c.generic_params); + this.check_lifetime_params(old_scope, &c.generic_params, None, &[]); intravisit::walk_ty(this, ty); }); self.is_in_fn_syntax = was_in_fn_syntax; @@ -871,7 +876,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { abstract_type_parent: false, }; let result = self.with(scope, |old_scope, this| { - this.check_lifetime_params(old_scope, &bound_generic_params); + this.check_lifetime_params(old_scope, &bound_generic_params, None, &[]); this.visit_ty(&bounded_ty); walk_list!(this, visit_ty_param_bound, bounds); }); @@ -938,7 +943,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { abstract_type_parent: false, }; self.with(scope, |old_scope, this| { - this.check_lifetime_params(old_scope, &trait_ref.bound_generic_params); + this.check_lifetime_params(old_scope, &trait_ref.bound_generic_params, None, &[]); walk_list!(this, visit_generic_param, &trait_ref.bound_generic_params); this.visit_trait_ref(&trait_ref.trait_ref) }) @@ -1441,6 +1446,18 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { .collect(); let next_early_index = index + generics.ty_params().count() as u32; + let mut arg_lifetimes = vec![]; + for input in &decl.inputs { + for lt in input.lifetimes() { + arg_lifetimes.push(lt); + } + } + if let hir::FunctionRetTy::Return(ref output) = decl.output { + for lt in output.lifetimes() { + arg_lifetimes.push(lt); + } + } + let scope = Scope::Binder { lifetimes, @@ -1450,7 +1467,12 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { track_lifetime_uses: false, }; self.with(scope, move |old_scope, this| { - this.check_lifetime_params(old_scope, &generics.params); + this.check_lifetime_params( + old_scope, + &generics.params, + Some(generics.span), + &arg_lifetimes, + ); this.hack(walk); // FIXME(#37666) workaround in place of `walk(this)` }); } @@ -2031,7 +2053,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { if let Some(params) = error { if lifetime_refs.len() == 1 { - self.report_elision_failure(&mut err, params); + self.report_elision_failure(&mut err, params, span); } } @@ -2042,6 +2064,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { &mut self, db: &mut DiagnosticBuilder, params: &[ElisionFailureInfo], + span: Span, ) { let mut m = String::new(); let len = params.len(); @@ -2097,7 +2120,11 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { "this function's return type contains a borrowed value, but \ there is no value for it to be borrowed from" ); - help!(db, "consider giving it a 'static lifetime"); + db.span_suggestion( + span, + "consider giving it a `'static` lifetime", + "&'static ".to_owned(), + ); } else if elided_len == 0 { help!( db, @@ -2105,10 +2132,10 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { an elided lifetime, but the lifetime cannot be derived from \ the arguments" ); - help!( - db, - "consider giving it an explicit bounded or 'static \ - lifetime" + db.span_suggestion( + span, + "consider giving it an explicit bound or `'static` lifetime", + "&'static ".to_owned(), ); } else if elided_len == 1 { help!( @@ -2149,82 +2176,147 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { self.insert_lifetime(lifetime_ref, lifetime.shifted(late_depth)); } - fn check_lifetime_params(&mut self, old_scope: ScopeRef, params: &'tcx [hir::GenericParam]) { - for (i, lifetime_i) in params.lifetimes().enumerate() { - match lifetime_i.lifetime.name { - hir::LifetimeName::Static | hir::LifetimeName::Underscore => { - let lifetime = lifetime_i.lifetime; - let name = lifetime.name.name(); - let mut err = struct_span_err!( - self.tcx.sess, - lifetime.span, - E0262, - "invalid lifetime parameter name: `{}`", - name - ); - err.span_label( - lifetime.span, - format!("{} is a reserved lifetime name", name), - ); - err.emit(); - } - hir::LifetimeName::Fresh(_) - | hir::LifetimeName::Implicit - | hir::LifetimeName::Name(_) => {} - } - - // It is a hard error to shadow a lifetime within the same scope. - for lifetime_j in params.lifetimes().skip(i + 1) { - if lifetime_i.lifetime.name == lifetime_j.lifetime.name { - struct_span_err!( - self.tcx.sess, - lifetime_j.lifetime.span, - E0263, - "lifetime name `{}` declared twice in the same scope", - lifetime_j.lifetime.name.name() - ).span_label(lifetime_j.lifetime.span, "declared twice") - .span_label(lifetime_i.lifetime.span, "previous declaration here") - .emit(); - } - } - - // It is a soft error to shadow a lifetime within a parent scope. - self.check_lifetime_def_for_shadowing(old_scope, &lifetime_i.lifetime); - - for bound in &lifetime_i.bounds { - match bound.name { - hir::LifetimeName::Underscore => { + fn check_lifetime_params( + &mut self, + old_scope: ScopeRef, + params: &'tcx [hir::GenericParam], + generics_span: Option, + arg_lifetimes: &[hir::Lifetime], + ) { + for (i, param_i) in params.iter().enumerate() { + if let hir::GenericParam::Lifetime(lifetime_i) = param_i { + match lifetime_i.lifetime.name { + hir::LifetimeName::Static | hir::LifetimeName::Underscore => { + let lifetime = lifetime_i.lifetime; + let name = lifetime.name.name(); let mut err = struct_span_err!( self.tcx.sess, - bound.span, - E0637, - "invalid lifetime bound name: `'_`" + lifetime.span, + E0262, + "invalid lifetime parameter name: `{}`", + name + ); + err.span_label( + lifetime.span, + format!("{} is a reserved lifetime name", name), ); - err.span_label(bound.span, "`'_` is a reserved lifetime name"); err.emit(); } - hir::LifetimeName::Static => { - self.insert_lifetime(bound, Region::Static); - self.tcx - .sess - .struct_span_warn( - lifetime_i.lifetime.span.to(bound.span), + hir::LifetimeName::Fresh(_) + | hir::LifetimeName::Implicit + | hir::LifetimeName::Name(_) => {} + } + + // It is a hard error to shadow a lifetime within the same scope. + for param_j in params.iter().skip(i + 1) { + if let hir::GenericParam::Lifetime(lifetime_j) = param_j { + if lifetime_i.lifetime.name == lifetime_j.lifetime.name { + struct_span_err!( + self.tcx.sess, + lifetime_j.lifetime.span, + E0263, + "lifetime name `{}` declared twice in the same scope", + lifetime_j.lifetime.name.name() + ).span_label(lifetime_j.lifetime.span, "declared twice") + .span_label(lifetime_i.lifetime.span, "previous declaration here") + .emit(); + } + } + } + + // It is a soft error to shadow a lifetime within a parent scope. + self.check_lifetime_def_for_shadowing(old_scope, &lifetime_i.lifetime); + + for bound in &lifetime_i.bounds { + match bound.name { + hir::LifetimeName::Underscore => { + let mut err = struct_span_err!( + self.tcx.sess, + bound.span, + E0637, + "invalid lifetime bound name: `'_`" + ); + err.span_label(bound.span, "`'_` is a reserved lifetime name"); + err.emit(); + } + hir::LifetimeName::Static => { + self.insert_lifetime(bound, Region::Static); + let sp = lifetime_i.lifetime.span.to(bound.span); + let mut warn = self.tcx.sess.struct_span_warn( + sp, &format!( "unnecessary lifetime parameter `{}`", lifetime_i.lifetime.name.name() ), - ) - .help(&format!( - "you can use the `'static` lifetime directly, in place \ - of `{}`", - lifetime_i.lifetime.name.name() - )) - .emit(); - } - hir::LifetimeName::Fresh(_) - | hir::LifetimeName::Implicit - | hir::LifetimeName::Name(_) => { - self.resolve_lifetime_ref(bound); + ); + + // all the use spans for this lifetime in arguments to be replaced + // with `'static` + let mut spans_to_replace = arg_lifetimes.iter().filter_map(|lifetime| { + if lifetime.name.name() == lifetime_i.lifetime.name.name() { + Some((lifetime.span, "'static".to_owned())) + } else { + None + } + }).collect::>(); + // all the use spans for this lifetime in lifetime bounds + for param in params.iter() { + if let hir::GenericParam::Lifetime(lifetime_def) = param { + for lifetime in &lifetime_def.bounds { + if lifetime.name.name() == lifetime_i.lifetime.name.name() { + spans_to_replace.push((lifetime.span, + "'static".to_owned())); + } + } + } + } + + if let (1, Some(sp)) = (params.len(), generics_span) { + spans_to_replace.push((sp, "".into())); + warn.multipart_suggestion( + &format!( + "you can use the `'static` lifetime directly, \ + in place of `{}`", + lifetime_i.lifetime.name.name(), + ), + spans_to_replace, + ); + } else if params.len() > 1 { + let sp = if let Some(next_param) = params.iter().nth(i + 1) { + // we'll remove everything until the next parameter + lifetime_i.lifetime.span.until(next_param.span()) + } else if let Some(prev_param) = params.iter().nth(i - 1) { + // this must be the last argument, include the previous comma + self.tcx.sess.codemap() + .next_point(prev_param.span()) + .to(sp) + } else { // THIS SHOULDN'T HAPPEN :| + sp + }; + + spans_to_replace.push((sp, "".into())); + + warn.multipart_suggestion( + &format!( + "you can use the `'static` lifetime directly, \ + in place of `{}`", + lifetime_i.lifetime.name.name(), + ), + spans_to_replace, + ); + } else { + warn.help(&format!( + "you can use the `'static` lifetime directly, in place of `{}`", + lifetime_i.lifetime.name.name(), + )); + } + warn.emit(); + } + hir::LifetimeName::Fresh(_) + | hir::LifetimeName::Implicit + | hir::LifetimeName::Name(_) => { + self.resolve_lifetime_ref(bound); + } } } } diff --git a/src/test/ui/issue-26638.stderr b/src/test/ui/issue-26638.stderr index cf6fcd9f01cc8..b4b1a5ca1b106 100644 --- a/src/test/ui/issue-26638.stderr +++ b/src/test/ui/issue-26638.stderr @@ -10,19 +10,23 @@ error[E0106]: missing lifetime specifier --> $DIR/issue-26638.rs:14:40 | LL | fn parse_type_2(iter: fn(&u8)->&u8) -> &str { iter() } - | ^ expected lifetime parameter + | ^ + | | + | expected lifetime parameter + | help: consider giving it an explicit bound or `'static` lifetime: `&'static` | = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments - = help: consider giving it an explicit bounded or 'static lifetime error[E0106]: missing lifetime specifier --> $DIR/issue-26638.rs:17:22 | LL | fn parse_type_3() -> &str { unimplemented!() } - | ^ expected lifetime parameter + | ^ + | | + | expected lifetime parameter + | help: consider giving it a `'static` lifetime: `&'static` | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from - = help: consider giving it a 'static lifetime error: aborting due to 3 previous errors diff --git a/src/test/ui/lifetime-elision-return-type-requires-explicit-lifetime.stderr b/src/test/ui/lifetime-elision-return-type-requires-explicit-lifetime.stderr index 30cff86ed1d40..c72f4aa746c3a 100644 --- a/src/test/ui/lifetime-elision-return-type-requires-explicit-lifetime.stderr +++ b/src/test/ui/lifetime-elision-return-type-requires-explicit-lifetime.stderr @@ -2,10 +2,12 @@ error[E0106]: missing lifetime specifier --> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:12:11 | LL | fn f() -> &isize { //~ ERROR missing lifetime specifier - | ^ expected lifetime parameter + | ^ + | | + | expected lifetime parameter + | help: consider giving it a `'static` lifetime: `&'static` | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from - = help: consider giving it a 'static lifetime error[E0106]: missing lifetime specifier --> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:17:33 @@ -27,28 +29,34 @@ error[E0106]: missing lifetime specifier --> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:31:20 | LL | fn i(_x: isize) -> &isize { //~ ERROR missing lifetime specifier - | ^ expected lifetime parameter + | ^ + | | + | expected lifetime parameter + | help: consider giving it an explicit bound or `'static` lifetime: `&'static` | = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments - = help: consider giving it an explicit bounded or 'static lifetime error[E0106]: missing lifetime specifier --> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:44:24 | LL | fn j(_x: StaticStr) -> &isize { //~ ERROR missing lifetime specifier - | ^ expected lifetime parameter + | ^ + | | + | expected lifetime parameter + | help: consider giving it an explicit bound or `'static` lifetime: `&'static` | = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments - = help: consider giving it an explicit bounded or 'static lifetime error[E0106]: missing lifetime specifier --> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:50:49 | LL | fn k<'a, T: WithLifetime<'a>>(_x: T::Output) -> &isize { - | ^ expected lifetime parameter + | ^ + | | + | expected lifetime parameter + | help: consider giving it an explicit bound or `'static` lifetime: `&'static` | = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments - = help: consider giving it an explicit bounded or 'static lifetime error: aborting due to 6 previous errors diff --git a/src/test/ui/suggestions/static-lifetime.rs b/src/test/ui/suggestions/static-lifetime.rs new file mode 100644 index 0000000000000..c114bb4e2469e --- /dev/null +++ b/src/test/ui/suggestions/static-lifetime.rs @@ -0,0 +1,41 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct Foo<'a>(&'a str); + +fn _foo<'a: 'static, 'b>(_x: &'a str, _y: &'b str) -> &'a str { +//~^ WARNING unnecessary lifetime parameter `'a` + "" +} + +fn _foo1<'b, 'a: 'static>(_x: &'a str, _y: Foo<'a>) -> &'a str { +//~^ WARNING unnecessary lifetime parameter `'a` + "" +} + +fn _foo2<'c, 'a: 'static, 'b>(_x: &'a str, _y: &'b str) -> &'a str { +//~^ WARNING unnecessary lifetime parameter `'a` + "" +} + +fn _foo3<'c, 'a: 'static, 'b: 'a, 'd>(_x: &'a str, _y: &'b str) -> &'a str { +//~^ WARNING unnecessary lifetime parameter `'a` + "" +} + +fn _foo4<'a: 'static>(_x: &'a str, _y: &str) -> &'a str { +//~^ WARNING unnecessary lifetime parameter `'a` + "" +} + +fn _foo5<'a: 'static, 'b>(_x: &'a str, _y: &'b str) -> &'b str { +//~^ WARNING unnecessary lifetime parameter `'a` + "" +} diff --git a/src/test/ui/suggestions/static-lifetime.stderr b/src/test/ui/suggestions/static-lifetime.stderr new file mode 100644 index 0000000000000..6e12166e84d85 --- /dev/null +++ b/src/test/ui/suggestions/static-lifetime.stderr @@ -0,0 +1,67 @@ +error[E0601]: `main` function not found in crate `static_lifetime` + | + = note: consider adding a `main` function to `$DIR/static-lifetime.rs` + +warning: unnecessary lifetime parameter `'a` + --> $DIR/static-lifetime.rs:13:9 + | +LL | fn _foo<'a: 'static, 'b>(_x: &'a str, _y: &'b str) -> &'a str { + | ^^^^^^^^^^^ +help: you can use the `'static` lifetime directly, in place of `'a` + | +LL | fn _foo<'b>(_x: &'static str, _y: &'b str) -> &'static str { + | -- ^^^^^^^ ^^^^^^^ + +warning: unnecessary lifetime parameter `'a` + --> $DIR/static-lifetime.rs:18:14 + | +LL | fn _foo1<'b, 'a: 'static>(_x: &'a str, _y: Foo<'a>) -> &'a str { + | ^^^^^^^^^^^ +help: you can use the `'static` lifetime directly, in place of `'a` + | +LL | fn _foo1<'b>(_x: &'static str, _y: Foo<'static>) -> &'static str { + | -- ^^^^^^^ ^^^^^^^ ^^^^^^^ + +warning: unnecessary lifetime parameter `'a` + --> $DIR/static-lifetime.rs:23:14 + | +LL | fn _foo2<'c, 'a: 'static, 'b>(_x: &'a str, _y: &'b str) -> &'a str { + | ^^^^^^^^^^^ +help: you can use the `'static` lifetime directly, in place of `'a` + | +LL | fn _foo2<'c, 'b>(_x: &'static str, _y: &'b str) -> &'static str { + | -- ^^^^^^^ ^^^^^^^ + +warning: unnecessary lifetime parameter `'a` + --> $DIR/static-lifetime.rs:28:14 + | +LL | fn _foo3<'c, 'a: 'static, 'b: 'a, 'd>(_x: &'a str, _y: &'b str) -> &'a str { + | ^^^^^^^^^^^ +help: you can use the `'static` lifetime directly, in place of `'a` + | +LL | fn _foo3<'c, 'b: 'static, 'd>(_x: &'static str, _y: &'b str) -> &'static str { + | -- ^^^^^^^ ^^^^^^^ ^^^^^^^ + +warning: unnecessary lifetime parameter `'a` + --> $DIR/static-lifetime.rs:33:10 + | +LL | fn _foo4<'a: 'static>(_x: &'a str, _y: &str) -> &'a str { + | ^^^^^^^^^^^ +help: you can use the `'static` lifetime directly, in place of `'a` + | +LL | fn _foo4(_x: &'static str, _y: &str) -> &'static str { + | -- ^^^^^^^ ^^^^^^^ + +warning: unnecessary lifetime parameter `'a` + --> $DIR/static-lifetime.rs:38:10 + | +LL | fn _foo5<'a: 'static, 'b>(_x: &'a str, _y: &'b str) -> &'b str { + | ^^^^^^^^^^^ +help: you can use the `'static` lifetime directly, in place of `'a` + | +LL | fn _foo5<'b>(_x: &'static str, _y: &'b str) -> &'b str { + | -- ^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0601`.