diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 2b3a538772ecb..2a246a99a7032 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1786,7 +1786,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { GenericArg::Lifetime(hir::Lifetime { hir_id: self.next_id(), span: self.lower_span(span), - name: hir::LifetimeName::Implicit, + name: hir::LifetimeName::Implicit(false), }))); let generic_args = self.arena.alloc_from_iter(generic_args); @@ -1927,7 +1927,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }); let param_name = match lt.name { hir::LifetimeName::Param(param_name) => param_name, - hir::LifetimeName::Implicit + hir::LifetimeName::Implicit(_) | hir::LifetimeName::Underscore | hir::LifetimeName::Static => hir::ParamName::Plain(lt.name.ident()), hir::LifetimeName::ImplicitObjectLifetimeDefault => { @@ -2290,7 +2290,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { AnonymousLifetimeMode::ReportError => self.new_error_lifetime(None, span), - AnonymousLifetimeMode::PassThrough => self.new_implicit_lifetime(span), + AnonymousLifetimeMode::PassThrough => self.new_implicit_lifetime(span, false), } } @@ -2322,11 +2322,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &'s mut self, span: Span, count: usize, + param_mode: ParamMode, ) -> impl Iterator + Captures<'a> + Captures<'s> + Captures<'hir> { - (0..count).map(move |_| self.elided_path_lifetime(span)) + (0..count).map(move |_| self.elided_path_lifetime(span, param_mode)) } - fn elided_path_lifetime(&mut self, span: Span) -> hir::Lifetime { + fn elided_path_lifetime(&mut self, span: Span, param_mode: ParamMode) -> hir::Lifetime { match self.anonymous_lifetime_mode { AnonymousLifetimeMode::CreateParameter => { // We should have emitted E0726 when processing this path above @@ -2342,7 +2343,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // lifetime. Instead, we simply create an implicit lifetime, which will be checked // later, at which point a suitable error will be emitted. AnonymousLifetimeMode::PassThrough | AnonymousLifetimeMode::ReportError => { - self.new_implicit_lifetime(span) + self.new_implicit_lifetime(span, param_mode == ParamMode::Explicit) } } } @@ -2385,11 +2386,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { r } - fn new_implicit_lifetime(&mut self, span: Span) -> hir::Lifetime { + fn new_implicit_lifetime(&mut self, span: Span, missing: bool) -> hir::Lifetime { hir::Lifetime { hir_id: self.next_id(), span: self.lower_span(span), - name: hir::LifetimeName::Implicit, + name: hir::LifetimeName::Implicit(missing), } } @@ -2536,7 +2537,7 @@ fn lifetimes_from_impl_trait_bounds( fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) { let name = match lifetime.name { - hir::LifetimeName::Implicit | hir::LifetimeName::Underscore => { + hir::LifetimeName::Implicit(_) | hir::LifetimeName::Underscore => { if self.collect_elided_lifetimes { // Use `'_` for both implicit and underscore lifetimes in // `type Foo<'_> = impl SomeTrait<'_>;`. diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 929f427484dba..cf0ee4fc28fe2 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -7,8 +7,6 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, PartialRes, Res}; use rustc_hir::def_id::DefId; use rustc_hir::GenericArg; -use rustc_session::lint::builtin::ELIDED_LIFETIMES_IN_PATHS; -use rustc_session::lint::BuiltinLintDiagnostics; use rustc_span::symbol::Ident; use rustc_span::{BytePos, Span, DUMMY_SP}; @@ -270,12 +268,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let has_lifetimes = generic_args.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))); - if !generic_args.parenthesized && !has_lifetimes { + if !generic_args.parenthesized && !has_lifetimes && expected_lifetimes > 0 { // Note: these spans are used for diagnostics when they can't be inferred. // See rustc_resolve::late::lifetimes::LifetimeContext::add_missing_lifetime_specifiers_label let elided_lifetime_span = if generic_args.span.is_empty() { // If there are no brackets, use the identifier span. - segment.ident.span + path_span } else if generic_args.is_empty() { // If there are brackets, but not generic arguments, then use the opening bracket generic_args.span.with_hi(generic_args.span.lo() + BytePos(1)) @@ -284,67 +282,47 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { generic_args.span.with_lo(generic_args.span.lo() + BytePos(1)).shrink_to_lo() }; generic_args.args = self - .elided_path_lifetimes(elided_lifetime_span, expected_lifetimes) + .elided_path_lifetimes(elided_lifetime_span, expected_lifetimes, param_mode) .map(GenericArg::Lifetime) .chain(generic_args.args.into_iter()) .collect(); - if expected_lifetimes > 0 && param_mode == ParamMode::Explicit { + // In create-parameter mode we error here because we don't want to support + // deprecated impl elision in new features like impl elision and `async fn`, + // both of which work using the `CreateParameter` mode: + // + // impl Foo for std::cell::Ref // note lack of '_ + // async fn foo(_: std::cell::Ref) { ... } + if let (ParamMode::Explicit, AnonymousLifetimeMode::CreateParameter) = + (param_mode, self.anonymous_lifetime_mode) + { let anon_lt_suggestion = vec!["'_"; expected_lifetimes].join(", "); let no_non_lt_args = generic_args.args.len() == expected_lifetimes; let no_bindings = generic_args.bindings.is_empty(); - let (incl_angl_brckt, insertion_sp, suggestion) = if no_non_lt_args && no_bindings { + let (incl_angl_brckt, suggestion) = if no_non_lt_args && no_bindings { // If there are no generic args, our suggestion can include the angle brackets. - (true, path_span.shrink_to_hi(), format!("<{}>", anon_lt_suggestion)) + (true, format!("<{}>", anon_lt_suggestion)) } else { // Otherwise we'll insert a `'_, ` right after the opening bracket. - let span = generic_args - .span - .with_lo(generic_args.span.lo() + BytePos(1)) - .shrink_to_lo(); - (false, span, format!("{}, ", anon_lt_suggestion)) + (false, format!("{}, ", anon_lt_suggestion)) }; - match self.anonymous_lifetime_mode { - // In create-parameter mode we error here because we don't want to support - // deprecated impl elision in new features like impl elision and `async fn`, - // both of which work using the `CreateParameter` mode: - // - // impl Foo for std::cell::Ref // note lack of '_ - // async fn foo(_: std::cell::Ref) { ... } - AnonymousLifetimeMode::CreateParameter => { - let mut err = struct_span_err!( - self.sess, - path_span, - E0726, - "implicit elided lifetime not allowed here" - ); - rustc_errors::add_elided_lifetime_in_path_suggestion( - &self.sess.source_map(), - &mut err, - expected_lifetimes, - path_span, - incl_angl_brckt, - insertion_sp, - suggestion, - ); - err.note("assuming a `'static` lifetime..."); - err.emit(); - } - AnonymousLifetimeMode::PassThrough | AnonymousLifetimeMode::ReportError => { - self.resolver.lint_buffer().buffer_lint_with_diagnostic( - ELIDED_LIFETIMES_IN_PATHS, - CRATE_NODE_ID, - path_span, - "hidden lifetime parameters in types are deprecated", - BuiltinLintDiagnostics::ElidedLifetimesInPaths( - expected_lifetimes, - path_span, - incl_angl_brckt, - insertion_sp, - suggestion, - ), - ); - } - } + let insertion_sp = elided_lifetime_span.shrink_to_hi(); + let mut err = struct_span_err!( + self.sess, + path_span, + E0726, + "implicit elided lifetime not allowed here" + ); + rustc_errors::add_elided_lifetime_in_path_suggestion( + &self.sess.source_map(), + &mut err, + expected_lifetimes, + path_span, + incl_angl_brckt, + insertion_sp, + suggestion, + ); + err.note("assuming a `'static` lifetime..."); + err.emit(); } } diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 5edb52b0b650d..64a7a25dc86ac 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -584,7 +584,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { Some(RegionNameHighlight::MatchedAdtAndSegment(lifetime_span)) } - hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Implicit => { + hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Implicit(_) => { // In this case, the user left off the lifetime; so // they wrote something like: // diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index c67d3df3dedd8..2466e69f83646 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -92,7 +92,9 @@ pub enum LifetimeName { Param(ParamName), /// User wrote nothing (e.g., the lifetime in `&u32`). - Implicit, + /// + /// The bool indicates whether the user should have written something. + Implicit(bool), /// Implicit lifetime in a context like `dyn Foo`. This is /// distinguished from implicit lifetimes elsewhere because the @@ -122,7 +124,7 @@ impl LifetimeName { pub fn ident(&self) -> Ident { match *self { LifetimeName::ImplicitObjectLifetimeDefault - | LifetimeName::Implicit + | LifetimeName::Implicit(_) | LifetimeName::Error => Ident::empty(), LifetimeName::Underscore => Ident::with_dummy_span(kw::UnderscoreLifetime), LifetimeName::Static => Ident::with_dummy_span(kw::StaticLifetime), @@ -133,7 +135,7 @@ impl LifetimeName { pub fn is_elided(&self) -> bool { match self { LifetimeName::ImplicitObjectLifetimeDefault - | LifetimeName::Implicit + | LifetimeName::Implicit(_) | LifetimeName::Underscore => true, // It might seem surprising that `Fresh(_)` counts as @@ -3298,7 +3300,7 @@ mod size_asserts { rustc_data_structures::static_assert_size!(super::Expr<'static>, 64); rustc_data_structures::static_assert_size!(super::Pat<'static>, 88); rustc_data_structures::static_assert_size!(super::QPath<'static>, 24); - rustc_data_structures::static_assert_size!(super::Ty<'static>, 72); + rustc_data_structures::static_assert_size!(super::Ty<'static>, 80); rustc_data_structures::static_assert_size!(super::Item<'static>, 184); rustc_data_structures::static_assert_size!(super::TraitItem<'static>, 128); diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index cff543760f42a..21f89104c4b58 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -545,7 +545,7 @@ pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime | LifetimeName::Param(ParamName::Error) | LifetimeName::Static | LifetimeName::Error - | LifetimeName::Implicit + | LifetimeName::Implicit(_) | LifetimeName::ImplicitObjectLifetimeDefault | LifetimeName::Underscore => {} } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index d506931b516e7..69697f275e180 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1871,6 +1871,117 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { err.emit(); } + /// Returns whether to add `'static` lifetime to the suggested lifetime list. + crate fn report_elision_failure( + &mut self, + db: &mut DiagnosticBuilder<'_>, + params: &[ElisionFailureInfo], + ) -> bool { + let mut m = String::new(); + let len = params.len(); + + let elided_params: Vec<_> = + params.iter().cloned().filter(|info| info.lifetime_count > 0).collect(); + + let elided_len = elided_params.len(); + + for (i, info) in elided_params.into_iter().enumerate() { + let ElisionFailureInfo { parent, index, lifetime_count: n, have_bound_regions, span } = + info; + + db.span_label(span, ""); + let help_name = if let Some(ident) = + parent.and_then(|body| self.tcx.hir().body(body).params[index].pat.simple_ident()) + { + format!("`{}`", ident) + } else { + format!("argument {}", index + 1) + }; + + m.push_str( + &(if n == 1 { + help_name + } else { + format!( + "one of {}'s {} {}lifetimes", + help_name, + n, + if have_bound_regions { "free " } else { "" } + ) + })[..], + ); + + if elided_len == 2 && i == 0 { + m.push_str(" or "); + } else if i + 2 == elided_len { + m.push_str(", or "); + } else if i != elided_len - 1 { + m.push_str(", "); + } + } + + if len == 0 { + db.help( + "this function's return type contains a borrowed value, \ + but there is no value for it to be borrowed from", + ); + true + } else if elided_len == 0 { + db.help( + "this function's return type contains a borrowed value with \ + an elided lifetime, but the lifetime cannot be derived from \ + the arguments", + ); + true + } else if elided_len == 1 { + db.help(&format!( + "this function's return type contains a borrowed value, \ + but the signature does not say which {} it is borrowed from", + m + )); + false + } else { + db.help(&format!( + "this function's return type contains a borrowed value, \ + but the signature does not say whether it is borrowed from {}", + m + )); + false + } + } + + crate fn report_elided_lifetime_in_ty(&self, lifetime_refs: &[&hir::Lifetime]) { + let Some(missing_lifetime) = lifetime_refs.iter().find(|lt| { + lt.name == hir::LifetimeName::Implicit(true) + }) else { return }; + + let mut spans: Vec<_> = lifetime_refs.iter().map(|lt| lt.span).collect(); + spans.sort(); + let mut spans_dedup = spans.clone(); + spans_dedup.dedup(); + let spans_with_counts: Vec<_> = spans_dedup + .into_iter() + .map(|sp| (sp, spans.iter().filter(|nsp| *nsp == &sp).count())) + .collect(); + + self.tcx.struct_span_lint_hir( + rustc_session::lint::builtin::ELIDED_LIFETIMES_IN_PATHS, + missing_lifetime.hir_id, + spans, + |lint| { + let mut db = lint.build("hidden lifetime parameters in types are deprecated"); + self.add_missing_lifetime_specifiers_label( + &mut db, + spans_with_counts, + &FxHashSet::from_iter([kw::UnderscoreLifetime]), + Vec::new(), + &[], + ); + db.emit() + }, + ); + } + // FIXME(const_generics): This patches over an ICE caused by non-'static lifetimes in const // generics. We are disallowing this until we can decide on how we want to handle non-'static // lifetimes in const generics. See issue #74052 for discussion. @@ -2297,7 +2408,9 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { ); let is_allowed_lifetime = matches!( lifetime_ref.name, - hir::LifetimeName::Implicit | hir::LifetimeName::Static | hir::LifetimeName::Underscore + hir::LifetimeName::Implicit(_) + | hir::LifetimeName::Static + | hir::LifetimeName::Underscore ); if !self.tcx.lazy_normalization() && is_anon_const && !is_allowed_lifetime { diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index f9d609f9c9ce6..1ff33689b53c7 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -357,11 +357,11 @@ enum Elide { #[derive(Clone, Debug)] crate struct ElisionFailureInfo { /// Where we can find the argument pattern. - parent: Option, + crate parent: Option, /// The index of the argument in the original definition. - index: usize, - lifetime_count: usize, - have_bound_regions: bool, + crate index: usize, + crate lifetime_count: usize, + crate have_bound_regions: bool, crate span: Span, } @@ -923,7 +923,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } }); match lifetime.name { - LifetimeName::Implicit => { + LifetimeName::Implicit(_) => { // For types like `dyn Foo`, we should // generate a special form of elided. span_bug!(ty.span, "object-lifetime-default expected, not implicit",); @@ -3057,9 +3057,9 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { let error = loop { match *scope { // Do not assign any resolution, it will be inferred. - Scope::Body { .. } => return, + Scope::Body { .. } => break Ok(()), - Scope::Root => break None, + Scope::Root => break Err(None), Scope::Binder { s, ref lifetimes, scope_type, .. } => { // collect named lifetimes for suggestions @@ -3076,50 +3076,54 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { scope = s; } - Scope::Elision { ref elide, ref s, .. } => { - let lifetime = match *elide { - Elide::FreshLateAnon(named_late_bound_vars, ref counter) => { - for lifetime_ref in lifetime_refs { - let lifetime = Region::late_anon(named_late_bound_vars, counter) - .shifted(late_depth); + Scope::Elision { + elide: Elide::FreshLateAnon(named_late_bound_vars, ref counter), + .. + } => { + for lifetime_ref in lifetime_refs { + let lifetime = + Region::late_anon(named_late_bound_vars, counter).shifted(late_depth); - self.insert_lifetime(lifetime_ref, lifetime); - } - return; - } - Elide::Exact(l) => l.shifted(late_depth), - Elide::Error(ref e) => { - let mut scope = s; - loop { - match scope { - Scope::Binder { ref lifetimes, s, .. } => { - // Collect named lifetimes for suggestions. - for name in lifetimes.keys() { - if let hir::ParamName::Plain(name) = name { - lifetime_names.insert(name.name); - lifetime_spans.push(name.span); - } - } - scope = s; - } - Scope::ObjectLifetimeDefault { ref s, .. } - | Scope::Elision { ref s, .. } - | Scope::TraitRefBoundary { ref s, .. } => { - scope = s; + self.insert_lifetime(lifetime_ref, lifetime); + } + break Ok(()); + } + + Scope::Elision { elide: Elide::Exact(l), .. } => { + let lifetime = l.shifted(late_depth); + for lifetime_ref in lifetime_refs { + self.insert_lifetime(lifetime_ref, lifetime); + } + break Ok(()); + } + + Scope::Elision { elide: Elide::Error(ref e), ref s, .. } => { + let mut scope = s; + loop { + match scope { + Scope::Binder { ref lifetimes, s, .. } => { + // Collect named lifetimes for suggestions. + for name in lifetimes.keys() { + if let hir::ParamName::Plain(name) = name { + lifetime_names.insert(name.name); + lifetime_spans.push(name.span); } - _ => break, } + scope = s; + } + Scope::ObjectLifetimeDefault { ref s, .. } + | Scope::Elision { ref s, .. } + | Scope::TraitRefBoundary { ref s, .. } => { + scope = s; } - break Some(&e[..]); + _ => break, } - Elide::Forbid => break None, - }; - for lifetime_ref in lifetime_refs { - self.insert_lifetime(lifetime_ref, lifetime); } - return; + break Err(Some(&e[..])); } + Scope::Elision { elide: Elide::Forbid, .. } => break Err(None), + Scope::ObjectLifetimeDefault { s, .. } | Scope::Supertrait { s, .. } | Scope::TraitRefBoundary { s, .. } => { @@ -3128,6 +3132,14 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } }; + let error = match error { + Ok(()) => { + self.report_elided_lifetime_in_ty(lifetime_refs); + return; + } + Err(error) => error, + }; + // If we specifically need the `scope_for_path` map, then we're in the // diagnostic pass and we don't want to emit more errors. if self.map.scope_for_path.is_some() { @@ -3166,84 +3178,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { err.emit(); } - fn report_elision_failure( - &mut self, - db: &mut DiagnosticBuilder<'_>, - params: &[ElisionFailureInfo], - ) -> bool /* add `'static` lifetime to lifetime list */ { - let mut m = String::new(); - let len = params.len(); - - let elided_params: Vec<_> = - params.iter().cloned().filter(|info| info.lifetime_count > 0).collect(); - - let elided_len = elided_params.len(); - - for (i, info) in elided_params.into_iter().enumerate() { - let ElisionFailureInfo { parent, index, lifetime_count: n, have_bound_regions, span } = - info; - - db.span_label(span, ""); - let help_name = if let Some(ident) = - parent.and_then(|body| self.tcx.hir().body(body).params[index].pat.simple_ident()) - { - format!("`{}`", ident) - } else { - format!("argument {}", index + 1) - }; - - m.push_str( - &(if n == 1 { - help_name - } else { - format!( - "one of {}'s {} {}lifetimes", - help_name, - n, - if have_bound_regions { "free " } else { "" } - ) - })[..], - ); - - if elided_len == 2 && i == 0 { - m.push_str(" or "); - } else if i + 2 == elided_len { - m.push_str(", or "); - } else if i != elided_len - 1 { - m.push_str(", "); - } - } - - if len == 0 { - db.help( - "this function's return type contains a borrowed value, \ - but there is no value for it to be borrowed from", - ); - true - } else if elided_len == 0 { - db.help( - "this function's return type contains a borrowed value with \ - an elided lifetime, but the lifetime cannot be derived from \ - the arguments", - ); - true - } else if elided_len == 1 { - db.help(&format!( - "this function's return type contains a borrowed value, \ - but the signature does not say which {} it is borrowed from", - m - )); - false - } else { - db.help(&format!( - "this function's return type contains a borrowed value, \ - but the signature does not say whether it is borrowed from {}", - m - )); - false - } - } - fn resolve_object_lifetime_default(&mut self, lifetime_ref: &'tcx hir::Lifetime) { debug!("resolve_object_lifetime_default(lifetime_ref={:?})", lifetime_ref); let mut late_depth = 0; @@ -3348,7 +3282,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { )) .emit(); } - hir::LifetimeName::Param(_) | hir::LifetimeName::Implicit => { + hir::LifetimeName::Param(_) | hir::LifetimeName::Implicit(_) => { self.resolve_lifetime_ref(lt); } hir::LifetimeName::ImplicitObjectLifetimeDefault => { diff --git a/src/test/ui/in-band-lifetimes/elided-lifetimes.fixed b/src/test/ui/in-band-lifetimes/elided-lifetimes.fixed index 2998f05efbae4..87a79b8813777 100644 --- a/src/test/ui/in-band-lifetimes/elided-lifetimes.fixed +++ b/src/test/ui/in-band-lifetimes/elided-lifetimes.fixed @@ -5,23 +5,24 @@ #![deny(elided_lifetimes_in_paths)] //~^ NOTE the lint level is defined here -use std::cell::{RefCell, Ref}; +use std::cell::{Ref, RefCell}; - -struct Foo<'a> { x: &'a u32 } +struct Foo<'a> { + x: &'a u32, +} fn foo(x: &Foo<'_>) { //~^ ERROR hidden lifetime parameters in types are deprecated - //~| HELP indicate the anonymous lifetime + //~| NOTE expected named lifetime parameter + //~| HELP consider using the `'_` lifetime } fn bar(x: &Foo<'_>) {} - struct Wrapped<'a>(&'a str); struct WrappedWithBow<'a> { - gift: &'a str + gift: &'a str, } struct MatchedSet<'a, 'b> { @@ -31,22 +32,34 @@ struct MatchedSet<'a, 'b> { fn wrap_gift(gift: &str) -> Wrapped<'_> { //~^ ERROR hidden lifetime parameters in types are deprecated - //~| HELP indicate the anonymous lifetime + //~| NOTE expected named lifetime parameter + //~| HELP consider using the `'_` lifetime Wrapped(gift) } fn wrap_gift_with_bow(gift: &str) -> WrappedWithBow<'_> { //~^ ERROR hidden lifetime parameters in types are deprecated - //~| HELP indicate the anonymous lifetime + //~| NOTE expected named lifetime parameter + //~| HELP consider using the `'_` lifetime WrappedWithBow { gift } } fn inspect_matched_set(set: MatchedSet<'_, '_>) { //~^ ERROR hidden lifetime parameters in types are deprecated - //~| HELP indicate the anonymous lifetime + //~| NOTE expected 2 lifetime parameters + //~| HELP consider using the `'_` lifetime println!("{} {}", set.one, set.another); } +// Verify that the lint does not fire, because the added `'_` wouldn't be resolved correctly. +fn match_sets() -> MatchedSet<'static, 'static> { + //~^ ERROR missing lifetime specifiers + //~| NOTE expected 2 lifetime parameters + //~| HELP this function's return type contains a borrowed value + //~| HELP consider using the `'static` lifetime + MatchedSet { one: "one", another: "another" } +} + macro_rules! autowrapper { ($type_name:ident, $fn_name:ident, $lt:lifetime) => { struct $type_name<$lt> { @@ -55,7 +68,11 @@ macro_rules! autowrapper { fn $fn_name(gift: &str) -> $type_name<'_> { //~^ ERROR hidden lifetime parameters in types are deprecated - //~| HELP indicate the anonymous lifetime + //~| NOTE expected named lifetime parameter + //~| HELP consider using the `'_` lifetime + //~| ERROR hidden lifetime parameters in types are deprecated + //~| NOTE expected named lifetime parameter + //~| HELP consider using the `'_` lifetime $type_name { gift } } } @@ -65,19 +82,34 @@ autowrapper!(Autowrapped, autowrap_gift, 'a); //~^ NOTE in this expansion of autowrapper! //~| NOTE in this expansion of autowrapper! +// Verify that rustfix does not try to apply the fix twice. +autowrapper!(AutowrappedAgain, autowrap_gift_again, 'a); +//~^ NOTE in this expansion of autowrapper! +//~| NOTE in this expansion of autowrapper! + macro_rules! anytuple_ref_ty { ($($types:ty),*) => { Ref<'_, ($($types),*)> //~^ ERROR hidden lifetime parameters in types are deprecated - //~| HELP indicate the anonymous lifetime + //~| NOTE expected named lifetime parameter + //~| HELP consider using the `'_` lifetime } } +#[allow(elided_lifetimes_in_paths)] +mod blah { + struct Thing<'a>(&'a i32); + struct Bar(T); + + fn foo(b: Bar) {} +} + fn main() { let honesty = RefCell::new((4, 'e')); let loyalty: Ref<'_, (u32, char)> = honesty.borrow(); //~^ ERROR hidden lifetime parameters in types are deprecated - //~| HELP indicate the anonymous lifetime + //~| NOTE expected named lifetime parameter + //~| HELP consider using the `'_` lifetime let generosity = Ref::map(loyalty, |t| &t.0); let laughter = RefCell::new((true, "magic")); diff --git a/src/test/ui/in-band-lifetimes/elided-lifetimes.rs b/src/test/ui/in-band-lifetimes/elided-lifetimes.rs index b729a15a29edd..28323a22427b6 100644 --- a/src/test/ui/in-band-lifetimes/elided-lifetimes.rs +++ b/src/test/ui/in-band-lifetimes/elided-lifetimes.rs @@ -5,23 +5,24 @@ #![deny(elided_lifetimes_in_paths)] //~^ NOTE the lint level is defined here -use std::cell::{RefCell, Ref}; +use std::cell::{Ref, RefCell}; - -struct Foo<'a> { x: &'a u32 } +struct Foo<'a> { + x: &'a u32, +} fn foo(x: &Foo) { //~^ ERROR hidden lifetime parameters in types are deprecated - //~| HELP indicate the anonymous lifetime + //~| NOTE expected named lifetime parameter + //~| HELP consider using the `'_` lifetime } fn bar(x: &Foo<'_>) {} - struct Wrapped<'a>(&'a str); struct WrappedWithBow<'a> { - gift: &'a str + gift: &'a str, } struct MatchedSet<'a, 'b> { @@ -31,22 +32,34 @@ struct MatchedSet<'a, 'b> { fn wrap_gift(gift: &str) -> Wrapped { //~^ ERROR hidden lifetime parameters in types are deprecated - //~| HELP indicate the anonymous lifetime + //~| NOTE expected named lifetime parameter + //~| HELP consider using the `'_` lifetime Wrapped(gift) } fn wrap_gift_with_bow(gift: &str) -> WrappedWithBow { //~^ ERROR hidden lifetime parameters in types are deprecated - //~| HELP indicate the anonymous lifetime + //~| NOTE expected named lifetime parameter + //~| HELP consider using the `'_` lifetime WrappedWithBow { gift } } fn inspect_matched_set(set: MatchedSet) { //~^ ERROR hidden lifetime parameters in types are deprecated - //~| HELP indicate the anonymous lifetime + //~| NOTE expected 2 lifetime parameters + //~| HELP consider using the `'_` lifetime println!("{} {}", set.one, set.another); } +// Verify that the lint does not fire, because the added `'_` wouldn't be resolved correctly. +fn match_sets() -> MatchedSet { + //~^ ERROR missing lifetime specifiers + //~| NOTE expected 2 lifetime parameters + //~| HELP this function's return type contains a borrowed value + //~| HELP consider using the `'static` lifetime + MatchedSet { one: "one", another: "another" } +} + macro_rules! autowrapper { ($type_name:ident, $fn_name:ident, $lt:lifetime) => { struct $type_name<$lt> { @@ -55,7 +68,11 @@ macro_rules! autowrapper { fn $fn_name(gift: &str) -> $type_name { //~^ ERROR hidden lifetime parameters in types are deprecated - //~| HELP indicate the anonymous lifetime + //~| NOTE expected named lifetime parameter + //~| HELP consider using the `'_` lifetime + //~| ERROR hidden lifetime parameters in types are deprecated + //~| NOTE expected named lifetime parameter + //~| HELP consider using the `'_` lifetime $type_name { gift } } } @@ -65,19 +82,34 @@ autowrapper!(Autowrapped, autowrap_gift, 'a); //~^ NOTE in this expansion of autowrapper! //~| NOTE in this expansion of autowrapper! +// Verify that rustfix does not try to apply the fix twice. +autowrapper!(AutowrappedAgain, autowrap_gift_again, 'a); +//~^ NOTE in this expansion of autowrapper! +//~| NOTE in this expansion of autowrapper! + macro_rules! anytuple_ref_ty { ($($types:ty),*) => { Ref<($($types),*)> //~^ ERROR hidden lifetime parameters in types are deprecated - //~| HELP indicate the anonymous lifetime + //~| NOTE expected named lifetime parameter + //~| HELP consider using the `'_` lifetime } } +#[allow(elided_lifetimes_in_paths)] +mod blah { + struct Thing<'a>(&'a i32); + struct Bar(T); + + fn foo(b: Bar) {} +} + fn main() { let honesty = RefCell::new((4, 'e')); let loyalty: Ref<(u32, char)> = honesty.borrow(); //~^ ERROR hidden lifetime parameters in types are deprecated - //~| HELP indicate the anonymous lifetime + //~| NOTE expected named lifetime parameter + //~| HELP consider using the `'_` lifetime let generosity = Ref::map(loyalty, |t| &t.0); let laughter = RefCell::new((true, "magic")); diff --git a/src/test/ui/in-band-lifetimes/elided-lifetimes.stderr b/src/test/ui/in-band-lifetimes/elided-lifetimes.stderr index 037ce401b3cc9..2e65461b321e9 100644 --- a/src/test/ui/in-band-lifetimes/elided-lifetimes.stderr +++ b/src/test/ui/in-band-lifetimes/elided-lifetimes.stderr @@ -1,60 +1,120 @@ error: hidden lifetime parameters in types are deprecated - --> $DIR/elided-lifetimes.rs:13:12 + --> $DIR/elided-lifetimes.rs:14:12 | LL | fn foo(x: &Foo) { - | ^^^- help: indicate the anonymous lifetime: `<'_>` + | ^^^ expected named lifetime parameter | note: the lint level is defined here --> $DIR/elided-lifetimes.rs:5:9 | LL | #![deny(elided_lifetimes_in_paths)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider using the `'_` lifetime + | +LL | fn foo(x: &Foo<'_>) { + | ~~~~~~~ error: hidden lifetime parameters in types are deprecated - --> $DIR/elided-lifetimes.rs:32:29 + --> $DIR/elided-lifetimes.rs:33:29 | LL | fn wrap_gift(gift: &str) -> Wrapped { - | ^^^^^^^- help: indicate the anonymous lifetime: `<'_>` + | ^^^^^^^ expected named lifetime parameter + | +help: consider using the `'_` lifetime + | +LL | fn wrap_gift(gift: &str) -> Wrapped<'_> { + | ~~~~~~~~~~~ error: hidden lifetime parameters in types are deprecated - --> $DIR/elided-lifetimes.rs:38:38 + --> $DIR/elided-lifetimes.rs:40:38 | LL | fn wrap_gift_with_bow(gift: &str) -> WrappedWithBow { - | ^^^^^^^^^^^^^^- help: indicate the anonymous lifetime: `<'_>` + | ^^^^^^^^^^^^^^ expected named lifetime parameter + | +help: consider using the `'_` lifetime + | +LL | fn wrap_gift_with_bow(gift: &str) -> WrappedWithBow<'_> { + | ~~~~~~~~~~~~~~~~~~ error: hidden lifetime parameters in types are deprecated - --> $DIR/elided-lifetimes.rs:44:29 + --> $DIR/elided-lifetimes.rs:47:29 | LL | fn inspect_matched_set(set: MatchedSet) { - | ^^^^^^^^^^- help: indicate the anonymous lifetimes: `<'_, '_>` + | ^^^^^^^^^^ expected 2 lifetime parameters + | +help: consider using the `'_` lifetime + | +LL | fn inspect_matched_set(set: MatchedSet<'_, '_>) { + | ~~~~~~~~~~~~~~~~~~ + +error[E0106]: missing lifetime specifiers + --> $DIR/elided-lifetimes.rs:55:20 + | +LL | fn match_sets() -> MatchedSet { + | ^^^^^^^^^^ expected 2 lifetime parameters + | + = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from +help: consider using the `'static` lifetime + | +LL | fn match_sets() -> MatchedSet<'static, 'static> { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: hidden lifetime parameters in types are deprecated - --> $DIR/elided-lifetimes.rs:56:36 + --> $DIR/elided-lifetimes.rs:69:36 | LL | fn $fn_name(gift: &str) -> $type_name { - | ^^^^^^^^^^- help: indicate the anonymous lifetime: `<'_>` + | ^^^^^^^^^^ expected named lifetime parameter ... LL | autowrapper!(Autowrapped, autowrap_gift, 'a); | -------------------------------------------- in this macro invocation | = note: this error originates in the macro `autowrapper` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider using the `'_` lifetime + | +LL | fn $fn_name(gift: &str) -> $type_name<'_> { + | ~~~~~~~~~~~~~~ error: hidden lifetime parameters in types are deprecated - --> $DIR/elided-lifetimes.rs:78:18 + --> $DIR/elided-lifetimes.rs:69:36 + | +LL | fn $fn_name(gift: &str) -> $type_name { + | ^^^^^^^^^^ expected named lifetime parameter +... +LL | autowrapper!(AutowrappedAgain, autowrap_gift_again, 'a); + | ------------------------------------------------------- in this macro invocation + | + = note: this error originates in the macro `autowrapper` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider using the `'_` lifetime + | +LL | fn $fn_name(gift: &str) -> $type_name<'_> { + | ~~~~~~~~~~~~~~ + +error: hidden lifetime parameters in types are deprecated + --> $DIR/elided-lifetimes.rs:109:22 | LL | let loyalty: Ref<(u32, char)> = honesty.borrow(); - | ^^^^^^^^^^^^^^^^ help: indicate the anonymous lifetime: `Ref<'_, (u32, char)>` + | ^ expected named lifetime parameter + | +help: consider using the `'_` lifetime + | +LL | let loyalty: Ref<'_, (u32, char)> = honesty.borrow(); + | +++ error: hidden lifetime parameters in types are deprecated - --> $DIR/elided-lifetimes.rs:70:9 + --> $DIR/elided-lifetimes.rs:92:13 | LL | Ref<($($types),*)> - | ^^^^^^^^^^^^^^^^^^ help: indicate the anonymous lifetime: `Ref<'_, ($($types),*)>` + | ^ expected named lifetime parameter ... LL | let yellow: anytuple_ref_ty!(bool, &str) = laughter.borrow(); | ---------------------------- in this macro invocation | = note: this error originates in the macro `anytuple_ref_ty` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider using the `'_` lifetime + | +LL | Ref<'_, ($($types),*)> + | +++ -error: aborting due to 7 previous errors +error: aborting due to 9 previous errors +For more information about this error, try `rustc --explain E0106`. diff --git a/src/test/ui/lint/force-warn/allowed-by-default-lint.stderr b/src/test/ui/lint/force-warn/allowed-by-default-lint.stderr index baa47cbb10fd5..f5e8b41b1634e 100644 --- a/src/test/ui/lint/force-warn/allowed-by-default-lint.stderr +++ b/src/test/ui/lint/force-warn/allowed-by-default-lint.stderr @@ -2,9 +2,13 @@ warning: hidden lifetime parameters in types are deprecated --> $DIR/allowed-by-default-lint.rs:9:12 | LL | fn foo(x: &Foo) {} - | ^^^- help: indicate the anonymous lifetime: `<'_>` + | ^^^ expected named lifetime parameter | = note: requested on the command line with `--force-warn elided-lifetimes-in-paths` +help: consider using the `'_` lifetime + | +LL | fn foo(x: &Foo<'_>) {} + | ~~~~~~~ warning: 1 warning emitted diff --git a/src/test/ui/lint/reasons.rs b/src/test/ui/lint/reasons.rs index 34cac4968a84f..b1792e2e9cbf8 100644 --- a/src/test/ui/lint/reasons.rs +++ b/src/test/ui/lint/reasons.rs @@ -1,7 +1,6 @@ // check-pass #![feature(lint_reasons)] - #![warn(elided_lifetimes_in_paths, //~^ NOTE the lint level is defined here reason = "explicit anonymous lifetimes aid reasoning about ownership")] @@ -20,8 +19,9 @@ pub struct CheaterDetectionMechanism {} impl fmt::Debug for CheaterDetectionMechanism { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { //~^ WARN hidden lifetime parameters in types are deprecated + //~| NOTE expected named lifetime parameter //~| NOTE explicit anonymous lifetimes aid - //~| HELP indicate the anonymous lifetime + //~| HELP consider using the `'_` lifetime fmt.debug_struct("CheaterDetectionMechanism").finish() } } diff --git a/src/test/ui/lint/reasons.stderr b/src/test/ui/lint/reasons.stderr index 150237c6be293..f797c89a03269 100644 --- a/src/test/ui/lint/reasons.stderr +++ b/src/test/ui/lint/reasons.stderr @@ -1,15 +1,19 @@ warning: hidden lifetime parameters in types are deprecated - --> $DIR/reasons.rs:21:29 + --> $DIR/reasons.rs:20:29 | LL | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - | ^^^^^^^^^^^^^^- help: indicate the anonymous lifetime: `<'_>` + | ^^^^^^^^^^^^^^ expected named lifetime parameter | = note: explicit anonymous lifetimes aid reasoning about ownership note: the lint level is defined here - --> $DIR/reasons.rs:5:9 + --> $DIR/reasons.rs:4:9 | LL | #![warn(elided_lifetimes_in_paths, | ^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider using the `'_` lifetime + | +LL | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + | ~~~~~~~~~~~~~~~~~~ warning: variable `Social_exchange_psychology` should have a snake case name --> $DIR/reasons.rs:30:9 @@ -20,7 +24,7 @@ LL | let Social_exchange_psychology = CheaterDetectionMechanism {}; = note: people shouldn't have to change their usual style habits to contribute to our project note: the lint level is defined here - --> $DIR/reasons.rs:9:5 + --> $DIR/reasons.rs:8:5 | LL | nonstandard_style, | ^^^^^^^^^^^^^^^^^