From 2fc94cefc5b784011e276a8d7893cc05e84dcdaa Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 16 Feb 2024 05:40:46 +1100 Subject: [PATCH 1/7] Remove an unnecessary `span_delayed_bug` call. The existing code calls a function that returns `Result<_, ErrorGuaranteed>`, and then calls `span_delayed_bug` pointlessly in the `Err` case. --- .../src/type_check/free_region_relations.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index 897918fb0a45b..86d20599a2a88 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -311,17 +311,14 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { // Add implied bounds from impl header. if matches!(tcx.def_kind(defining_ty_def_id), DefKind::AssocFn | DefKind::AssocConst) { for &(ty, _) in tcx.assumed_wf_types(tcx.local_parent(defining_ty_def_id)) { - let Ok(TypeOpOutput { output: norm_ty, constraints: c, .. }) = self + let result: Result<_, ErrorGuaranteed> = self .param_env .and(type_op::normalize::Normalize::new(ty)) - .fully_perform(self.infcx, span) - else { - // Note: this path is currently not reached in any test, so - // any example that triggers this would be worth minimizing - // and converting into a test. - tcx.dcx().span_delayed_bug(span, format!("failed to normalize {ty:?}")); + .fully_perform(self.infcx, span); + let Ok(TypeOpOutput { output: norm_ty, constraints: c, .. }) = result else { continue; }; + constraints.extend(c); // We currently add implied bounds from the normalized ty only. From c743e4a855645af740258bbe5d0f707099ab3235 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 16 Feb 2024 06:32:13 +1100 Subject: [PATCH 2/7] Refactor `LoweringContext::get_delegation_sig_id`. I find the function much easier to read this way. Thanks to @kadiwa4 for the suggestion. --- compiler/rustc_ast_lowering/src/delegation.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index 77dd03d15f520..4e1c477a3d771 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -103,12 +103,8 @@ impl<'hir> LoweringContext<'_, 'hir> { span: Span, ) -> Result { let sig_id = if self.is_in_trait_impl { item_id } else { path_id }; - let sig_id = self - .resolver - .get_partial_res(sig_id) - .map(|r| r.expect_full_res().opt_def_id()) - .unwrap_or(None); - + let sig_id = + self.resolver.get_partial_res(sig_id).and_then(|r| r.expect_full_res().opt_def_id()); sig_id.ok_or_else(|| { self.tcx .dcx() From d0267cb26bad39685d43d8c8a3f6cec79ceac0f7 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 16 Feb 2024 14:19:07 +1100 Subject: [PATCH 3/7] Remove an unnecessary `span_delayed_bug` in `Resolver::valid_res_from_ribs`. `Resolver::report_error` always emits (this commit makes that clearer), so the `span_delayed_bug` is unnecessary. --- compiler/rustc_resolve/src/diagnostics.rs | 8 ++++++-- compiler/rustc_resolve/src/ident.rs | 5 ++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 222dd69dbc48e..96025a26de7ae 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -554,8 +554,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { /// /// This takes the error provided, combines it with the span and any additional spans inside the /// error and emits it. - pub(crate) fn report_error(&mut self, span: Span, resolution_error: ResolutionError<'a>) { - self.into_struct_error(span, resolution_error).emit(); + pub(crate) fn report_error( + &mut self, + span: Span, + resolution_error: ResolutionError<'a>, + ) -> ErrorGuaranteed { + self.into_struct_error(span, resolution_error).emit() } pub(crate) fn into_struct_error( diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 7e7424be303ac..c17ea00ec8094 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -1,4 +1,5 @@ use rustc_ast::{self as ast, NodeId}; +use rustc_errors::ErrorGuaranteed; use rustc_hir::def::{DefKind, Namespace, NonMacroAttrKind, PartialRes, PerNS}; use rustc_middle::bug; use rustc_middle::ty; @@ -1066,7 +1067,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { original_rib_ident_def: Ident, all_ribs: &[Rib<'a>], ) -> Res { - const CG_BUG_STR: &str = "min_const_generics resolve check didn't stop compilation"; debug!("validate_res_from_ribs({:?})", res); let ribs = &all_ribs[rib_index + 1..]; @@ -1209,8 +1209,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } }; - self.report_error(span, error); - self.dcx().span_delayed_bug(span, CG_BUG_STR); + let _: ErrorGuaranteed = self.report_error(span, error); } return Res::Err; From b2d1d6f6ffa4beee08dc189edb785411a9d578e0 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 16 Feb 2024 16:40:51 +1100 Subject: [PATCH 4/7] Avoid `span_delayed_bug` on one path in `AdtDef::eval_explicit_discr`. Also change its return type to `Result`. --- compiler/rustc_hir_analysis/src/collect.rs | 2 +- compiler/rustc_middle/src/ty/adt.rs | 37 ++++++++++++++-------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index e9c9ec6ba53f8..324369945d73d 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -760,7 +760,7 @@ fn convert_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId) { let wrapped_discr = prev_discr.map_or(initial, |d| d.wrap_incr(tcx)); prev_discr = Some( if let ty::VariantDiscr::Explicit(const_def_id) = variant.discr { - def.eval_explicit_discr(tcx, const_def_id) + def.eval_explicit_discr(tcx, const_def_id).ok() } else if let Some(discr) = repr_type.disr_incr(tcx, prev_discr) { Some(discr) } else { diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index d07a53ee6794d..2e1c7df6454aa 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -7,6 +7,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::intern::Interned; use rustc_data_structures::stable_hasher::HashingControls; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; @@ -475,7 +476,11 @@ impl<'tcx> AdtDef<'tcx> { } #[inline] - pub fn eval_explicit_discr(self, tcx: TyCtxt<'tcx>, expr_did: DefId) -> Option> { + pub fn eval_explicit_discr( + self, + tcx: TyCtxt<'tcx>, + expr_did: DefId, + ) -> Result, ErrorGuaranteed> { assert!(self.is_enum()); let param_env = tcx.param_env(expr_did); let repr_type = self.repr().discr_type(); @@ -484,22 +489,24 @@ impl<'tcx> AdtDef<'tcx> { let ty = repr_type.to_ty(tcx); if let Some(b) = val.try_to_bits_for_ty(tcx, param_env, ty) { trace!("discriminants: {} ({:?})", b, repr_type); - Some(Discr { val: b, ty }) + Ok(Discr { val: b, ty }) } else { info!("invalid enum discriminant: {:#?}", val); - tcx.dcx().emit_err(crate::error::ConstEvalNonIntError { + let guar = tcx.dcx().emit_err(crate::error::ConstEvalNonIntError { span: tcx.def_span(expr_did), }); - None + Err(guar) } } Err(err) => { - let msg = match err { - ErrorHandled::Reported(..) => "enum discriminant evaluation failed", - ErrorHandled::TooGeneric(..) => "enum discriminant depends on generics", + let guar = match err { + ErrorHandled::Reported(info, _) => info.into(), + ErrorHandled::TooGeneric(..) => tcx.dcx().span_delayed_bug( + tcx.def_span(expr_did), + "enum discriminant depends on generics", + ), }; - tcx.dcx().span_delayed_bug(tcx.def_span(expr_did), msg); - None + Err(guar) } } } @@ -516,7 +523,7 @@ impl<'tcx> AdtDef<'tcx> { self.variants().iter_enumerated().map(move |(i, v)| { let mut discr = prev_discr.map_or(initial, |d| d.wrap_incr(tcx)); if let VariantDiscr::Explicit(expr_did) = v.discr { - if let Some(new_discr) = self.eval_explicit_discr(tcx, expr_did) { + if let Ok(new_discr) = self.eval_explicit_discr(tcx, expr_did) { discr = new_discr; } } @@ -544,9 +551,13 @@ impl<'tcx> AdtDef<'tcx> { ) -> Discr<'tcx> { assert!(self.is_enum()); let (val, offset) = self.discriminant_def_for_variant(variant_index); - let explicit_value = val - .and_then(|expr_did| self.eval_explicit_discr(tcx, expr_did)) - .unwrap_or_else(|| self.repr().discr_type().initial_discriminant(tcx)); + let explicit_value = if let Some(expr_did) = val + && let Ok(val) = self.eval_explicit_discr(tcx, expr_did) + { + val + } else { + self.repr().discr_type().initial_discriminant(tcx) + }; explicit_value.checked_add(tcx, offset as u128).0 } From 9f825637183a52f9e069cb692051dda4a152b0ba Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 16 Feb 2024 17:07:57 +1100 Subject: [PATCH 5/7] Avoid a `span_delayed_bug` in `TypeErrCtxt::report_region_errors`. By returning error guarantees from a few functions it relies on. --- .../src/infer/error_reporting/mod.rs | 55 +++++++++---------- .../error_reporting/nice_region_error/mod.rs | 7 ++- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 7715f2ef43a16..9a130ed7f543d 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -428,6 +428,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { generic_param_scope: LocalDefId, errors: &[RegionResolutionError<'tcx>], ) -> ErrorGuaranteed { + assert!(!errors.is_empty()); + if let Some(guaranteed) = self.infcx.tainted_by_errors() { return guaranteed; } @@ -440,10 +442,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { debug!("report_region_errors: {} errors after preprocessing", errors.len()); + let mut guar = None; for error in errors { debug!("report_region_errors: error = {:?}", error); - if !self.try_report_nice_region_error(&error) { + guar = Some(if let Some(guar) = self.try_report_nice_region_error(&error) { + guar + } else { match error.clone() { // These errors could indicate all manner of different // problems with many different solutions. Rather @@ -454,21 +459,20 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // general bit of code that displays the error information RegionResolutionError::ConcreteFailure(origin, sub, sup) => { if sub.is_placeholder() || sup.is_placeholder() { - self.report_placeholder_failure(origin, sub, sup).emit(); + self.report_placeholder_failure(origin, sub, sup).emit() } else { - self.report_concrete_failure(origin, sub, sup).emit(); + self.report_concrete_failure(origin, sub, sup).emit() } } - RegionResolutionError::GenericBoundFailure(origin, param_ty, sub) => { - self.report_generic_bound_failure( + RegionResolutionError::GenericBoundFailure(origin, param_ty, sub) => self + .report_generic_bound_failure( generic_param_scope, origin.span(), Some(origin), param_ty, sub, - ); - } + ), RegionResolutionError::SubSupConflict( _, @@ -480,13 +484,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { _, ) => { if sub_r.is_placeholder() { - self.report_placeholder_failure(sub_origin, sub_r, sup_r).emit(); + self.report_placeholder_failure(sub_origin, sub_r, sup_r).emit() } else if sup_r.is_placeholder() { - self.report_placeholder_failure(sup_origin, sub_r, sup_r).emit(); + self.report_placeholder_failure(sup_origin, sub_r, sup_r).emit() } else { self.report_sub_sup_conflict( var_origin, sub_origin, sub_r, sup_origin, sup_r, - ); + ) } } @@ -506,7 +510,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // value. let sub_r = self.tcx.lifetimes.re_erased; - self.report_placeholder_failure(sup_origin, sub_r, sup_r).emit(); + self.report_placeholder_failure(sup_origin, sub_r, sup_r).emit() } RegionResolutionError::CannotNormalize(clause, origin) => { @@ -515,15 +519,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { self.tcx .dcx() .struct_span_err(origin.span(), format!("cannot normalize `{clause}`")) - .emit(); + .emit() } } - } + }) } - self.tcx - .dcx() - .span_delayed_bug(self.tcx.def_span(generic_param_scope), "expected region errors") + guar.unwrap() } // This method goes through all the errors and try to group certain types @@ -2314,9 +2316,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { origin: Option>, bound_kind: GenericKind<'tcx>, sub: Region<'tcx>, - ) { + ) -> ErrorGuaranteed { self.construct_generic_bound_failure(generic_param_scope, span, origin, bound_kind, sub) - .emit(); + .emit() } pub fn construct_generic_bound_failure( @@ -2575,7 +2577,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { sub_region: Region<'tcx>, sup_origin: SubregionOrigin<'tcx>, sup_region: Region<'tcx>, - ) { + ) -> ErrorGuaranteed { let mut err = self.report_inference_failure(var_origin); note_and_explain_region( @@ -2614,12 +2616,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); err.note_expected_found(&"", sup_expected, &"", sup_found); - if sub_region.is_error() | sup_region.is_error() { - err.delay_as_bug(); + return if sub_region.is_error() | sup_region.is_error() { + err.delay_as_bug() } else { - err.emit(); - } - return; + err.emit() + }; } self.note_region_origin(&mut err, &sup_origin); @@ -2634,11 +2635,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); self.note_region_origin(&mut err, &sub_origin); - if sub_region.is_error() | sup_region.is_error() { - err.delay_as_bug(); - } else { - err.emit(); - } + if sub_region.is_error() | sup_region.is_error() { err.delay_as_bug() } else { err.emit() } } /// Determine whether an error associated with the given span and definition diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs index d3e28446ddef1..57d284a49f853 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs @@ -21,8 +21,11 @@ pub use static_impl_trait::{suggest_new_region_bound, HirTraitObjectVisitor, Tra pub use util::find_param_with_region; impl<'cx, 'tcx> TypeErrCtxt<'cx, 'tcx> { - pub fn try_report_nice_region_error(&'cx self, error: &RegionResolutionError<'tcx>) -> bool { - NiceRegionError::new(self, error.clone()).try_report().is_some() + pub fn try_report_nice_region_error( + &'cx self, + error: &RegionResolutionError<'tcx>, + ) -> Option { + NiceRegionError::new(self, error.clone()).try_report() } } From 62b4e551124ffc38196740efd6ff3b6979a504eb Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 19 Feb 2024 08:42:04 +1100 Subject: [PATCH 6/7] Avoid a `span_delayed_bug` in `compute_regions`. By storing error guarantees in `RegionErrors`. --- .../src/diagnostics/region_errors.rs | 18 +++++++++++------- compiler/rustc_borrowck/src/nll.rs | 7 ++----- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index e586c58781cf1..cd70ec580a363 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -1,7 +1,7 @@ //! Error reporting machinery for lifetime errors. use rustc_data_structures::fx::FxIndexSet; -use rustc_errors::{Applicability, DiagnosticBuilder, MultiSpan}; +use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::Res::Def; use rustc_hir::def_id::DefId; @@ -73,7 +73,7 @@ impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> { /// /// Usually we expect this to either be empty or contain a small number of items, so we can avoid /// allocation most of the time. -pub(crate) struct RegionErrors<'tcx>(Vec>, TyCtxt<'tcx>); +pub(crate) struct RegionErrors<'tcx>(Vec<(RegionErrorKind<'tcx>, ErrorGuaranteed)>, TyCtxt<'tcx>); impl<'tcx> RegionErrors<'tcx> { pub fn new(tcx: TyCtxt<'tcx>) -> Self { @@ -82,15 +82,18 @@ impl<'tcx> RegionErrors<'tcx> { #[track_caller] pub fn push(&mut self, val: impl Into>) { let val = val.into(); - self.1.sess.dcx().delayed_bug(format!("{val:?}")); - self.0.push(val); + let guar = self.1.sess.dcx().delayed_bug(format!("{val:?}")); + self.0.push((val, guar)); } pub fn is_empty(&self) -> bool { self.0.is_empty() } - pub fn into_iter(self) -> impl Iterator> { + pub fn into_iter(self) -> impl Iterator, ErrorGuaranteed)> { self.0.into_iter() } + pub fn has_errors(&self) -> Option { + self.0.get(0).map(|x| x.1) + } } impl std::fmt::Debug for RegionErrors<'_> { @@ -304,10 +307,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let mut last_unexpected_hidden_region: Option<(Span, Ty<'_>, ty::OpaqueTypeKey<'tcx>)> = None; - for nll_error in nll_errors.into_iter() { + for (nll_error, _) in nll_errors.into_iter() { match nll_error { RegionErrorKind::TypeTestError { type_test } => { - // Try to convert the lower-bound region into something named we can print for the user. + // Try to convert the lower-bound region into something named we can print for + // the user. let lower_bound_region = self.to_error_region(type_test.lower_bound); let type_test_span = type_test.span; diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 80d79e1beb708..4aa32a61f7c36 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -184,12 +184,9 @@ pub(crate) fn compute_regions<'cx, 'tcx>( let (closure_region_requirements, nll_errors) = regioncx.solve(infcx, body, polonius_output.clone()); - if !nll_errors.is_empty() { + if let Some(guar) = nll_errors.has_errors() { // Suppress unhelpful extra errors in `infer_opaque_types`. - infcx.set_tainted_by_errors(infcx.dcx().span_delayed_bug( - body.span, - "`compute_regions` tainted `infcx` with errors but did not emit any errors", - )); + infcx.set_tainted_by_errors(guar); } let remapped_opaque_tys = regioncx.infer_opaque_types(infcx, opaque_type_values); From a8a486c846e29e663f7ee433a61613b98815cf4d Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 19 Feb 2024 09:59:56 +1100 Subject: [PATCH 7/7] Refactor `take_for_recovery` call sites. To make them more concise and similar to each other. --- compiler/rustc_parse/src/parser/expr.rs | 29 +++++++++++-------------- compiler/rustc_parse/src/parser/stmt.rs | 3 +-- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index e1a5e17004f6a..f5d4f4f57b99e 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2688,23 +2688,20 @@ impl<'a> Parser<'a> { branch_span: Span, attrs: AttrWrapper, ) { - if attrs.is_empty() { - return; + if !attrs.is_empty() + && let [x0 @ xn] | [x0, .., xn] = &*attrs.take_for_recovery(self.sess) + { + let attributes = x0.span.to(xn.span); + let last = xn.span; + let ctx = if is_ctx_else { "else" } else { "if" }; + self.dcx().emit_err(errors::OuterAttributeNotAllowedOnIfElse { + last, + branch_span, + ctx_span, + ctx: ctx.to_string(), + attributes, + }); } - - let attrs: &[ast::Attribute] = &attrs.take_for_recovery(self.sess); - let (attributes, last) = match attrs { - [] => return, - [x0 @ xn] | [x0, .., xn] => (x0.span.to(xn.span), xn.span), - }; - let ctx = if is_ctx_else { "else" } else { "if" }; - self.dcx().emit_err(errors::OuterAttributeNotAllowedOnIfElse { - last, - branch_span, - ctx_span, - ctx: ctx.to_string(), - attributes, - }); } fn error_on_extra_if(&mut self, cond: &P) -> PResult<'a, ()> { diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 15f8124823fb0..55d6f7d628163 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -230,8 +230,7 @@ impl<'a> Parser<'a> { /// Also error if the previous token was a doc comment. fn error_outer_attrs(&self, attrs: AttrWrapper) { if !attrs.is_empty() - && let attrs = attrs.take_for_recovery(self.sess) - && let attrs @ [.., last] = &*attrs + && let attrs @ [.., last] = &*attrs.take_for_recovery(self.sess) { if last.is_doc_comment() { self.dcx().emit_err(errors::DocCommentDoesNotDocumentAnything {