diff options
| author | Camille GILLOT <gillot.camille@gmail.com> | 2022-03-10 23:12:35 +0100 |
|---|---|---|
| committer | Camille GILLOT <gillot.camille@gmail.com> | 2022-04-17 11:03:34 +0200 |
| commit | a9e13fa5533df7fc004aeab00d45822eacb8461e (patch) | |
| tree | 6d759bc7958cc987f346f6e7f1d00b3487fc7b01 | |
| parent | ca57bada05373ff227c661cc542e99c92b70c0ee (diff) | |
| download | rust-a9e13fa5533df7fc004aeab00d45822eacb8461e.tar.gz rust-a9e13fa5533df7fc004aeab00d45822eacb8461e.zip | |
Lint elided lifetimes in path on the AST.
24 files changed, 279 insertions, 154 deletions
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 5f465124a31..9cb205074e7 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1835,7 +1835,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // Output lifetime like `'_`: for (span, node_id) in lifetimes_to_define { let param = this.fresh_lifetime_to_generic_param(span, node_id); - lifetime_params.push((span, hir::LifetimeName::Implicit(false))); + lifetime_params.push((span, hir::LifetimeName::Implicit)); generic_params.push(param); } let generic_params = this.arena.alloc_from_iter(generic_params); @@ -2017,7 +2017,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::Underscore => { + hir::LifetimeName::Implicit | hir::LifetimeName::Underscore => { hir::ParamName::Plain(lt.name.ident()) } hir::LifetimeName::ImplicitObjectLifetimeDefault => { @@ -2397,7 +2397,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { AnonymousLifetimeMode::ReportError => self.new_error_lifetime(None, span), - AnonymousLifetimeMode::PassThrough => self.new_implicit_lifetime(span, false), + AnonymousLifetimeMode::PassThrough => self.new_implicit_lifetime(span), } } @@ -2416,12 +2416,11 @@ 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, param_mode)) + (0..count).map(move |_| self.elided_path_lifetime(span)) } - fn elided_path_lifetime(&mut self, span: Span, param_mode: ParamMode) -> hir::Lifetime { + fn elided_path_lifetime(&mut self, span: Span) -> hir::Lifetime { match self.anonymous_lifetime_mode { AnonymousLifetimeMode::CreateParameter => { // We should have emitted E0726 when processing this path above @@ -2437,7 +2436,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, param_mode == ParamMode::Explicit) + self.new_implicit_lifetime(span) } } } @@ -2480,11 +2479,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { r } - fn new_implicit_lifetime(&mut self, span: Span, missing: bool) -> hir::Lifetime { + fn new_implicit_lifetime(&mut self, span: Span) -> hir::Lifetime { hir::Lifetime { hir_id: self.next_id(), span: self.lower_span(span), - name: hir::LifetimeName::Implicit(missing), + name: hir::LifetimeName::Implicit, } } } @@ -2587,7 +2586,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 b35e3a07161..8bf4b8fcc39 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -290,47 +290,16 @@ 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, param_mode) + .elided_path_lifetimes(elided_lifetime_span, expected_lifetimes) .map(GenericArg::Lifetime) .chain(generic_args.args.into_iter()) .collect(); - // 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, suggestion) = if no_non_lt_args && no_bindings { - // If there are no generic args, our suggestion can include the angle brackets. - (true, format!("<{}>", anon_lt_suggestion)) - } else { - // Otherwise we'll insert a `'_, ` right after the opening bracket. - (false, format!("{}, ", anon_lt_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(); + // Late resolver should have issued the error. + self.sess + .delay_span_bug(elided_lifetime_span, "implicit lifetime not allowed here"); } } diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 7742813888e..459d4a783e4 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -575,7 +575,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_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 416bc4e2e3b..d7c5df7d8e2 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -1511,35 +1511,17 @@ pub fn add_elided_lifetime_in_path_suggestion( path_span: Span, incl_angl_brckt: bool, insertion_span: Span, - anon_lts: String, ) { - let (replace_span, suggestion) = if incl_angl_brckt { - (insertion_span, anon_lts) - } else { - // When possible, prefer a suggestion that replaces the whole - // `Path<T>` expression with `Path<'_, T>`, rather than inserting `'_, ` - // at a point (which makes for an ugly/confusing label) - if let Ok(snippet) = source_map.span_to_snippet(path_span) { - // But our spans can get out of whack due to macros; if the place we think - // we want to insert `'_` isn't even within the path expression's span, we - // should bail out of making any suggestion rather than panicking on a - // subtract-with-overflow or string-slice-out-out-bounds (!) - // FIXME: can we do better? - if insertion_span.lo().0 < path_span.lo().0 { - return; - } - let insertion_index = (insertion_span.lo().0 - path_span.lo().0) as usize; - if insertion_index > snippet.len() { - return; - } - let (before, after) = snippet.split_at(insertion_index); - (path_span, format!("{}{}{}", before, anon_lts, after)) - } else { - (insertion_span, anon_lts) - } - }; - diag.span_suggestion( - replace_span, + diag.span_label(path_span, format!("expected lifetime parameter{}", pluralize!(n))); + if source_map.span_to_snippet(insertion_span).is_err() { + // Do not try to suggest anything if generated by a proc-macro. + return; + } + let anon_lts = vec!["'_"; n].join(", "); + let suggestion = + if incl_angl_brckt { format!("<{}>", anon_lts) } else { format!("{}, ", anon_lts) }; + diag.span_suggestion_verbose( + insertion_span.shrink_to_hi(), &format!("indicate the anonymous lifetime{}", pluralize!(n)), suggestion, Applicability::MachineApplicable, diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 83193746432..b3de86662eb 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -95,7 +95,7 @@ pub enum LifetimeName { /// User wrote nothing (e.g., the lifetime in `&u32`). /// /// The bool indicates whether the user should have written something. - Implicit(bool), + Implicit, /// Implicit lifetime in a context like `dyn Foo`. This is /// distinguished from implicit lifetimes elsewhere because the @@ -125,7 +125,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), @@ -136,7 +136,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 diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 445b856e513..8689e2c2afa 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -528,7 +528,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_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 3600b6ad212..152c53dac7f 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -21,7 +21,8 @@ use crate::passes::{EarlyLintPassObject, LateLintPassObject}; use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync; -use rustc_errors::{struct_span_err, Applicability, MultiSpan, SuggestionStyle}; +use rustc_errors::{add_elided_lifetime_in_path_suggestion, struct_span_err}; +use rustc_errors::{Applicability, MultiSpan, SuggestionStyle}; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def_id::{CrateNum, DefId}; @@ -665,6 +666,21 @@ pub trait LintContext: Sized { ) => { db.span_note(span_def, "the macro is defined here"); } + BuiltinLintDiagnostics::ElidedLifetimesInPaths( + n, + path_span, + incl_angl_brckt, + insertion_span, + ) => { + add_elided_lifetime_in_path_suggestion( + sess.source_map(), + &mut db, + n, + path_span, + incl_angl_brckt, + insertion_span, + ); + } BuiltinLintDiagnostics::UnknownCrateTypes(span, note, sugg) => { db.span_suggestion(span, ¬e, sugg, Applicability::MaybeIncorrect); } diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index e7855f8057c..b4262f184c8 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -284,6 +284,11 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> ast_visit::walk_path(self, p); } + fn visit_path_segment(&mut self, path_span: Span, s: &'a ast::PathSegment) { + self.check_id(s.id); + ast_visit::walk_path_segment(self, path_span, s); + } + fn visit_attribute(&mut self, attr: &'a ast::Attribute) { run_early_pass!(self, check_attribute, attr); } diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 031b01af5dd..4debbf26be6 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -418,6 +418,7 @@ pub enum BuiltinLintDiagnostics { AbsPathWithModule(Span), ProcMacroDeriveResolutionFallback(Span), MacroExpandedMacroExportsAccessedByAbsolutePaths(Span), + ElidedLifetimesInPaths(usize, Span, bool, Span), UnknownCrateTypes(Span, String, String), UnusedImports(String, Vec<(Span, String)>, Option<Span>), RedundantImport(Vec<(Span, bool)>, Ident), diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 54e8c03156d..063b15e643d 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -521,11 +521,10 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { // while the current crate doesn't have a valid `crate_name`. if crate_name != kw::Empty { // `crate_name` should not be interpreted as relative. - module_path.push(Segment { - ident: Ident { name: kw::PathRoot, span: source.ident.span }, - id: Some(self.r.next_node_id()), - has_generic_args: false, - }); + module_path.push(Segment::from_ident_and_id( + Ident { name: kw::PathRoot, span: source.ident.span }, + self.r.next_node_id(), + )); source.ident.name = crate_name; } if rename.is_none() { diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 24b6d656981..84fe0ec83d2 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -1402,7 +1402,7 @@ impl<'a> Resolver<'a> { let mut allow_super = true; let mut second_binding = None; - for (i, &Segment { ident, id, has_generic_args: _ }) in path.iter().enumerate() { + for (i, &Segment { ident, id, .. }) in path.iter().enumerate() { debug!("resolve_path ident {} {:?} {:?}", i, ident, id); let record_segment_res = |this: &mut Self, res| { if finalize.is_some() { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index c6cebd1d3b1..9bc5d63ca17 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -21,10 +21,11 @@ use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, DefKind, PartialRes, PerNS}; use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX}; use rustc_hir::{PrimTy, TraitCandidate}; +use rustc_middle::ty::DefIdTree; use rustc_middle::{bug, span_bug}; use rustc_session::lint; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::Span; +use rustc_span::{BytePos, Span}; use smallvec::{smallvec, SmallVec}; use rustc_span::source_map::{respan, Spanned}; @@ -1167,6 +1168,134 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.resolve_anonymous_lifetime(<, true); } + #[tracing::instrument(level = "debug", skip(self))] + fn resolve_elided_lifetimes_in_path( + &mut self, + path_id: NodeId, + partial_res: PartialRes, + path: &[Segment], + source: PathSource<'_>, + finalize: Finalize, + ) { + let Some(path_span) = finalize.path_span() else { + return; + }; + let proj_start = path.len() - partial_res.unresolved_segments(); + for (i, segment) in path.iter().enumerate() { + if segment.has_lifetime_args { + continue; + } + let Some(segment_id) = segment.id else { + continue; + }; + + // Figure out if this is a type/trait segment, + // which may need lifetime elision performed. + let type_def_id = match partial_res.base_res() { + Res::Def(DefKind::AssocTy, def_id) if i + 2 == proj_start => { + self.r.parent(def_id).unwrap() + } + Res::Def(DefKind::Variant, def_id) if i + 1 == proj_start => { + self.r.parent(def_id).unwrap() + } + Res::Def(DefKind::Struct, def_id) + | Res::Def(DefKind::Union, def_id) + | Res::Def(DefKind::Enum, def_id) + | Res::Def(DefKind::TyAlias, def_id) + | Res::Def(DefKind::Trait, def_id) + if i + 1 == proj_start => + { + def_id + } + _ => continue, + }; + + let expected_lifetimes = self.r.item_generics_num_lifetimes(type_def_id); + if expected_lifetimes == 0 { + continue; + } + + let missing = match source { + PathSource::Trait(..) | PathSource::TraitItem(..) | PathSource::Type => true, + PathSource::Expr(..) + | PathSource::Pat + | PathSource::Struct + | PathSource::TupleStruct(..) => false, + }; + let mut error = false; + for rib in self.lifetime_ribs.iter().rev() { + match rib.kind { + // 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>) { ... } + LifetimeRibKind::AnonymousCreateParameter => { + error = true; + break; + } + // `PassThrough` is the normal case. + // `new_error_lifetime`, which would usually be used in the case of `ReportError`, + // is unsuitable here, as these can occur from missing lifetime parameters in a + // `PathSegment`, for which there is no associated `'_` or `&T` with no explicit + // lifetime. Instead, we simply create an implicit lifetime, which will be checked + // later, at which point a suitable error will be emitted. + LifetimeRibKind::AnonymousPassThrough + | LifetimeRibKind::AnonymousReportError + | LifetimeRibKind::Item => break, + _ => {} + } + } + + if !missing { + continue; + } + + let elided_lifetime_span = if segment.has_generic_args { + // If there are brackets, but not generic arguments, then use the opening bracket + segment.args_span.with_hi(segment.args_span.lo() + BytePos(1)) + } else { + // If there are no brackets, use the identifier span. + // HACK: we use find_ancestor_inside to properly suggest elided spans in paths + // originating from macros, since the segment's span might be from a macro arg. + segment.ident.span.find_ancestor_inside(path_span).unwrap_or(path_span) + }; + if error { + let sess = self.r.session; + let mut err = rustc_errors::struct_span_err!( + sess, + path_span, + E0726, + "implicit elided lifetime not allowed here" + ); + rustc_errors::add_elided_lifetime_in_path_suggestion( + sess.source_map(), + &mut err, + expected_lifetimes, + path_span, + !segment.has_generic_args, + elided_lifetime_span, + ); + err.note("assuming a `'static` lifetime..."); + err.emit(); + } else { + self.r.lint_buffer.buffer_lint_with_diagnostic( + lint::builtin::ELIDED_LIFETIMES_IN_PATHS, + segment_id, + elided_lifetime_span, + "hidden lifetime parameters in types are deprecated", + lint::BuiltinLintDiagnostics::ElidedLifetimesInPaths( + expected_lifetimes, + path_span, + !segment.has_generic_args, + elided_lifetime_span, + ), + ); + } + } + } + /// Searches the current set of local scopes for labels. Returns the `NodeId` of the resolved /// label and reports an error if the label is not found or is unreachable. fn resolve_label(&self, mut label: Ident) -> Option<NodeId> { @@ -2528,6 +2657,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.r.record_partial_res(id, partial_res); } + self.resolve_elided_lifetimes_in_path(id, partial_res, path, source, finalize); partial_res } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 51a35f8f26d..0bae141ce26 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1986,38 +1986,6 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { } } - 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. @@ -2452,9 +2420,7 @@ 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 72ca9918e12..d5f2e2db1e3 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -921,7 +921,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",); @@ -2955,9 +2955,9 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { let error = loop { match *scope { // Do not assign any resolution, it will be inferred. - Scope::Body { .. } => break Ok(()), + Scope::Body { .. } => return, - Scope::Root => break Err(None), + Scope::Root => break None, Scope::Binder { s, ref lifetimes, scope_type, .. } => { // collect named lifetimes for suggestions @@ -2984,7 +2984,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { self.insert_lifetime(lifetime_ref, lifetime); } - break Ok(()); + return; } Scope::Elision { elide: Elide::Exact(l), .. } => { @@ -2992,7 +2992,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { for lifetime_ref in lifetime_refs { self.insert_lifetime(lifetime_ref, lifetime); } - break Ok(()); + return; } Scope::Elision { elide: Elide::Error(ref e), ref s, .. } => { @@ -3017,10 +3017,10 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { _ => break, } } - break Err(Some(&e[..])); + break Some(&e[..]); } - Scope::Elision { elide: Elide::Forbid, .. } => break Err(None), + Scope::Elision { elide: Elide::Forbid, .. } => break None, Scope::ObjectLifetimeDefault { s, .. } | Scope::Supertrait { s, .. } @@ -3030,14 +3030,6 @@ 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() { @@ -3174,7 +3166,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/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 7262d150f4c..eed54370e23 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -28,7 +28,7 @@ pub use rustc_hir::def::{Namespace, PerNS}; use rustc_arena::{DroplessArena, TypedArena}; use rustc_ast::node_id::NodeMap; use rustc_ast::{self as ast, NodeId, CRATE_NODE_ID}; -use rustc_ast::{Crate, Expr, ExprKind, LitKind, Path}; +use rustc_ast::{AngleBracketedArg, Crate, Expr, ExprKind, GenericArg, GenericArgs, LitKind, Path}; use rustc_ast_lowering::ResolverAstLowering; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::intern::Interned; @@ -283,6 +283,9 @@ pub struct Segment { /// Signals whether this `PathSegment` has generic arguments. Used to avoid providing /// nonsensical suggestions. has_generic_args: bool, + /// Signals whether this `PathSegment` has lifetime arguments. + has_lifetime_args: bool, + args_span: Span, } impl Segment { @@ -291,7 +294,23 @@ impl Segment { } fn from_ident(ident: Ident) -> Segment { - Segment { ident, id: None, has_generic_args: false } + Segment { + ident, + id: None, + has_generic_args: false, + has_lifetime_args: false, + args_span: DUMMY_SP, + } + } + + fn from_ident_and_id(ident: Ident, id: NodeId) -> Segment { + Segment { + ident, + id: Some(id), + has_generic_args: false, + has_lifetime_args: false, + args_span: DUMMY_SP, + } } fn names_to_string(segments: &[Segment]) -> String { @@ -301,7 +320,28 @@ impl Segment { impl<'a> From<&'a ast::PathSegment> for Segment { fn from(seg: &'a ast::PathSegment) -> Segment { - Segment { ident: seg.ident, id: Some(seg.id), has_generic_args: seg.args.is_some() } + let has_generic_args = seg.args.is_some(); + let (args_span, has_lifetime_args) = if let Some(args) = seg.args.as_deref() { + match args { + GenericArgs::AngleBracketed(args) => { + let found_lifetimes = args + .args + .iter() + .any(|arg| matches!(arg, AngleBracketedArg::Arg(GenericArg::Lifetime(_)))); + (args.span, found_lifetimes) + } + GenericArgs::Parenthesized(args) => (args.span, true), + } + } else { + (DUMMY_SP, false) + }; + Segment { + ident: seg.ident, + id: Some(seg.id), + has_generic_args, + has_lifetime_args, + args_span, + } } } diff --git a/src/test/ui/async-await/async-fn-path-elision.stderr b/src/test/ui/async-await/async-fn-path-elision.stderr index 3d18d9c4125..5e0c8c29989 100644 --- a/src/test/ui/async-await/async-fn-path-elision.stderr +++ b/src/test/ui/async-await/async-fn-path-elision.stderr @@ -2,9 +2,13 @@ error[E0726]: implicit elided lifetime not allowed here --> $DIR/async-fn-path-elision.rs:5:20 | LL | async fn error(lt: HasLifetime) { - | ^^^^^^^^^^^- help: indicate the anonymous lifetime: `<'_>` + | ^^^^^^^^^^^ expected lifetime parameter | = note: assuming a `'static` lifetime... +help: indicate the anonymous lifetime + | +LL | async fn error(lt: HasLifetime<'_>) { + | ++++ error: aborting due to previous error diff --git a/src/test/ui/impl-header-lifetime-elision/path-elided.stderr b/src/test/ui/impl-header-lifetime-elision/path-elided.stderr index 90522a885ab..0b7d3f1e851 100644 --- a/src/test/ui/impl-header-lifetime-elision/path-elided.stderr +++ b/src/test/ui/impl-header-lifetime-elision/path-elided.stderr @@ -2,9 +2,13 @@ error[E0726]: implicit elided lifetime not allowed here --> $DIR/path-elided.rs:7:18 | LL | impl MyTrait for Foo { - | ^^^- help: indicate the anonymous lifetime: `<'_>` + | ^^^ expected lifetime parameter | = note: assuming a `'static` lifetime... +help: indicate the anonymous lifetime + | +LL | impl MyTrait for Foo<'_> { + | ++++ error: aborting due to previous error diff --git a/src/test/ui/impl-header-lifetime-elision/trait-elided.stderr b/src/test/ui/impl-header-lifetime-elision/trait-elided.stderr index be918d0a30c..412bba6be71 100644 --- a/src/test/ui/impl-header-lifetime-elision/trait-elided.stderr +++ b/src/test/ui/impl-header-lifetime-elision/trait-elided.stderr @@ -2,9 +2,13 @@ error[E0726]: implicit elided lifetime not allowed here --> $DIR/trait-elided.rs:5:6 | LL | impl MyTrait for u32 {} - | ^^^^^^^- help: indicate the anonymous lifetime: `<'_>` + | ^^^^^^^ expected lifetime parameter | = note: assuming a `'static` lifetime... +help: indicate the anonymous lifetime + | +LL | impl MyTrait<'_> for u32 {} + | ++++ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-10412.stderr b/src/test/ui/issues/issue-10412.stderr index a65005fd72d..46b9fd541ad 100644 --- a/src/test/ui/issues/issue-10412.stderr +++ b/src/test/ui/issues/issue-10412.stderr @@ -44,9 +44,13 @@ error[E0726]: implicit elided lifetime not allowed here --> $DIR/issue-10412.rs:7:13 | LL | impl<'self> Serializable<str> for &'self str { - | ^^^^^^^^^^^^^^^^^ help: indicate the anonymous lifetime: `Serializable<'_, str>` + | ^^^^^^^^^^^^^^^^^ expected lifetime parameter | = note: assuming a `'static` lifetime... +help: indicate the anonymous lifetime + | +LL | impl<'self> Serializable<'_, str> for &'self str { + | +++ error[E0277]: the size for values of type `str` cannot be known at compilation time --> $DIR/issue-10412.rs:7:13 diff --git a/src/test/ui/lifetimes/issue-91763.stderr b/src/test/ui/lifetimes/issue-91763.stderr index 1b1912c8e45..6ccf008c003 100644 --- a/src/test/ui/lifetimes/issue-91763.stderr +++ b/src/test/ui/lifetimes/issue-91763.stderr @@ -2,13 +2,17 @@ error: hidden lifetime parameters in types are deprecated --> $DIR/issue-91763.rs:8:20 | LL | fn f() -> Ptr<Thing>; - | ^ expected named lifetime parameter + | ^ expected lifetime parameter | note: the lint level is defined here --> $DIR/issue-91763.rs:3:9 | LL | #![deny(elided_lifetimes_in_paths)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ +help: indicate the anonymous lifetime + | +LL | fn f() -> Ptr<Thing><'_>; + | ++++ error: aborting due to previous error 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 f5e8b41b163..ac98b5896ca 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,13 +2,13 @@ warning: hidden lifetime parameters in types are deprecated --> $DIR/allowed-by-default-lint.rs:9:12 | LL | fn foo(x: &Foo) {} - | ^^^ expected named lifetime parameter + | ^^^ expected lifetime parameter | = note: requested on the command line with `--force-warn elided-lifetimes-in-paths` -help: consider using the `'_` lifetime +help: indicate the anonymous 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 b1792e2e9cb..da1c740c4a3 100644 --- a/src/test/ui/lint/reasons.rs +++ b/src/test/ui/lint/reasons.rs @@ -19,9 +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 expected lifetime parameter //~| NOTE explicit anonymous lifetimes aid - //~| HELP consider using the `'_` lifetime + //~| HELP indicate the anonymous lifetime fmt.debug_struct("CheaterDetectionMechanism").finish() } } diff --git a/src/test/ui/lint/reasons.stderr b/src/test/ui/lint/reasons.stderr index a692d6af703..cd8412153f1 100644 --- a/src/test/ui/lint/reasons.stderr +++ b/src/test/ui/lint/reasons.stderr @@ -2,7 +2,9 @@ warning: hidden lifetime parameters in types are deprecated --> $DIR/reasons.rs:20:34 | LL | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - | ^^^^^^^^^ expected named lifetime parameter + | -----^^^^^^^^^ + | | + | expected lifetime parameter | = note: explicit anonymous lifetimes aid reasoning about ownership note: the lint level is defined here @@ -10,10 +12,10 @@ note: the lint level is defined here | LL | #![warn(elided_lifetimes_in_paths, | ^^^^^^^^^^^^^^^^^^^^^^^^^ -help: consider using the `'_` lifetime +help: indicate the anonymous 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 diff --git a/src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr b/src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr index ba624507c21..16d19872552 100644 --- a/src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr +++ b/src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr @@ -2,9 +2,13 @@ error[E0726]: implicit elided lifetime not allowed here --> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:13:16 | LL | impl Trait for Ref {} - | ^^^- help: indicate the anonymous lifetime: `<'_>` + | ^^^ expected lifetime parameter | = note: assuming a `'static` lifetime... +help: indicate the anonymous lifetime + | +LL | impl Trait for Ref<'_> {} + | ++++ error: incompatible lifetime on type --> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:16:21 |
