diff options
| author | bors <bors@rust-lang.org> | 2021-12-01 23:22:43 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2021-12-01 23:22:43 +0000 |
| commit | 76938d64a42304e4413842656c044e9b40a6041b (patch) | |
| tree | d361d767a2519c7fd14acb95efad519ee7f0292d | |
| parent | 48a5999fceeea84a8971634355287faa349909d4 (diff) | |
| parent | aa2450f41bc49752c45b1b7d51a4b71098c0c9e8 (diff) | |
| download | rust-76938d64a42304e4413842656c044e9b40a6041b.tar.gz rust-76938d64a42304e4413842656c044e9b40a6041b.zip | |
Auto merge of #90446 - cjgillot:late-elided, r=jackh726
Lint elided lifetimes in path during lifetime resolution. The lifetime elision lint is known to be brittle and can be redundant with later lifetime resolution errors. This PR aims to remove the redundancy by performing the lint after lifetime resolution. This PR proposes to carry the information that an elision should be linted against by using a special `LifetimeName`. I am not certain this is the best solution, but it is certainly the easiest. Fixes https://github.com/rust-lang/rust/issues/60199 Fixes https://github.com/rust-lang/rust/issues/55768 Fixes https://github.com/rust-lang/rust/issues/63110 Fixes https://github.com/rust-lang/rust/issues/71957
| -rw-r--r-- | compiler/rustc_ast_lowering/src/lib.rs | 19 | ||||
| -rw-r--r-- | compiler/rustc_ast_lowering/src/path.rs | 88 | ||||
| -rw-r--r-- | compiler/rustc_borrowck/src/diagnostics/region_name.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_hir/src/hir.rs | 10 | ||||
| -rw-r--r-- | compiler/rustc_hir/src/intravisit.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_resolve/src/late/diagnostics.rs | 115 | ||||
| -rw-r--r-- | compiler/rustc_resolve/src/late/lifetimes.rs | 178 | ||||
| -rw-r--r-- | src/test/ui/in-band-lifetimes/elided-lifetimes.fixed | 56 | ||||
| -rw-r--r-- | src/test/ui/in-band-lifetimes/elided-lifetimes.rs | 56 | ||||
| -rw-r--r-- | src/test/ui/in-band-lifetimes/elided-lifetimes.stderr | 90 | ||||
| -rw-r--r-- | src/test/ui/lint/force-warn/allowed-by-default-lint.stderr | 6 | ||||
| -rw-r--r-- | src/test/ui/lint/reasons.rs | 4 | ||||
| -rw-r--r-- | src/test/ui/lint/reasons.stderr | 12 |
13 files changed, 399 insertions, 239 deletions
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 2b3a538772e..2a246a99a70 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<Item = hir::Lifetime> + 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 929f427484d..cf0ee4fc28f 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<u32> // note lack of '_ + // async fn foo(_: std::cell::Ref<u32>) { ... } + 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<u32> // note lack of '_ - // async fn foo(_: std::cell::Ref<u32>) { ... } - 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 5edb52b0b65..64a7a25dc86 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 c67d3df3ded..2466e69f836 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 cff543760f4..21f89104c4b 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 d506931b516..69697f275e1 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 f9d609f9c9c..1ff33689b53 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<hir::BodyId>, + crate parent: Option<hir::BodyId>, /// 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 2998f05efba..87a79b88137 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>(T); + + fn foo(b: Bar<Thing>) {} +} + 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 b729a15a29e..28323a22427 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>(T); + + fn foo(b: Bar<Thing>) {} +} + 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 037ce401b3c..2e65461b321 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 baa47cbb10f..f5e8b41b163 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 34cac4968a8..b1792e2e9cb 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 150237c6be2..f797c89a032 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 | #