diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index e57f8da26769..94000454caaa 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1067,7 +1067,7 @@ fn validate_generic_param_order(dcx: DiagCtxtHandle<'_>, generics: &[GenericPara impl<'a> Visitor<'a> for AstValidator<'a> { fn visit_attribute(&mut self, attr: &Attribute) { - validate_attr::check_attr(&self.sess.psess, attr, self.lint_node_id); + validate_attr::check_attr(&self.sess.psess, attr); } fn visit_ty(&mut self, ty: &'a Ty) { diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index a8aa63bd05ee..e47fa075fbf7 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -398,6 +398,7 @@ fn parse_cfg_attr_internal<'a>( .into_boxed_slice(), span: attribute.span, }, + Some(attribute.get_normal_item().unsafety), ParsedDescription::Attribute, pred_span, CRATE_NODE_ID, diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs index 8006fb963b19..0c0915558089 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs @@ -63,6 +63,7 @@ pub fn parse_cfg_select( segments: vec![Ident::from_str("cfg_select")].into_boxed_slice(), span: cfg_span, }, + None, ParsedDescription::Macro, cfg_span, lint_node_id, diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index b7a6a1ef6d66..c6d8411e8356 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use rustc_ast as ast; -use rustc_ast::{AttrStyle, NodeId}; +use rustc_ast::{AttrStyle, NodeId, Safety}; use rustc_errors::DiagCtxtHandle; use rustc_feature::{AttributeTemplate, Features}; use rustc_hir::attrs::AttributeKind; @@ -146,6 +146,7 @@ impl<'sess> AttributeParser<'sess, Early> { normal_attr.item.span(), attr.style, path.get_attribute_path(), + Some(normal_attr.item.unsafety), ParsedDescription::Attribute, target_span, target_node_id, @@ -165,6 +166,7 @@ impl<'sess> AttributeParser<'sess, Early> { inner_span: Span, attr_style: AttrStyle, attr_path: AttrPath, + attr_safety: Option, parsed_description: ParsedDescription, target_span: Span, target_node_id: NodeId, @@ -181,14 +183,24 @@ impl<'sess> AttributeParser<'sess, Early> { sess, stage: Early { emit_errors }, }; + let mut emit_lint = |lint| { + crate::lints::emit_attribute_lint(&lint, sess); + }; + if let Some(safety) = attr_safety { + parser.check_attribute_safety( + &attr_path, + inner_span, + safety, + &mut emit_lint, + target_node_id, + ) + } let mut cx: AcceptContext<'_, 'sess, Early> = AcceptContext { shared: SharedContext { cx: &mut parser, target_span, target_id: target_node_id, - emit_lint: &mut |lint| { - crate::lints::emit_attribute_lint(&lint, sess); - }, + emit_lint: &mut emit_lint, }, attr_span, inner_span, @@ -289,6 +301,14 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { ast::AttrKind::Normal(n) => { attr_paths.push(PathParser(Cow::Borrowed(&n.item.path))); + self.check_attribute_safety( + &AttrPath::from_ast(&n.item.path), + lower_span(n.item.span()), + n.item.unsafety, + &mut emit_lint, + target_id, + ); + let parts = n.item.path.segments.iter().map(|seg| seg.ident.name).collect::>(); @@ -312,7 +332,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { emit_lint: &mut emit_lint, }, attr_span: lower_span(attr.span), - inner_span: lower_span(attr.get_normal_item().span()), + inner_span: lower_span(n.item.span()), attr_style: attr.style, parsed_description: ParsedDescription::Attribute, template: &accept.template, diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index 046cca4c742b..7a7f2555287a 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -98,6 +98,7 @@ mod interface; pub mod parser; mod lints; +mod safety; mod session_diagnostics; mod target_checking; pub mod validate_attr; diff --git a/compiler/rustc_attr_parsing/src/lints.rs b/compiler/rustc_attr_parsing/src/lints.rs index 3a2a37046696..a23884d7f71e 100644 --- a/compiler/rustc_attr_parsing/src/lints.rs +++ b/compiler/rustc_attr_parsing/src/lints.rs @@ -98,5 +98,17 @@ pub fn emit_attribute_lint(lint: &AttributeLint, lint_emi }, ) } + &AttributeLintKind::UnsafeAttrOutsideUnsafe { + attribute_name_span, + sugg_spans: (left, right), + } => lint_emitter.emit_node_span_lint( + rustc_session::lint::builtin::UNSAFE_ATTR_OUTSIDE_UNSAFE, + *id, + *span, + session_diagnostics::UnsafeAttrOutsideUnsafeLint { + span: attribute_name_span, + suggestion: session_diagnostics::UnsafeAttrOutsideUnsafeSuggestion { left, right }, + }, + ), } } diff --git a/compiler/rustc_attr_parsing/src/safety.rs b/compiler/rustc_attr_parsing/src/safety.rs new file mode 100644 index 000000000000..ff385bf13aaa --- /dev/null +++ b/compiler/rustc_attr_parsing/src/safety.rs @@ -0,0 +1,116 @@ +use rustc_ast::Safety; +use rustc_feature::{AttributeSafety, BUILTIN_ATTRIBUTE_MAP}; +use rustc_hir::AttrPath; +use rustc_hir::lints::{AttributeLint, AttributeLintKind}; +use rustc_span::{Span, sym}; + +use crate::context::Stage; +use crate::{AttributeParser, ShouldEmit}; + +impl<'sess, S: Stage> AttributeParser<'sess, S> { + pub fn check_attribute_safety( + &mut self, + attr_path: &AttrPath, + attr_span: Span, + attr_safety: Safety, + emit_lint: &mut impl FnMut(AttributeLint), + target_id: S::Id, + ) { + if matches!(self.stage.should_emit(), ShouldEmit::Nothing) { + return; + } + + let name = (attr_path.segments.len() == 1).then_some(attr_path.segments[0].name); + if let Some(name) = name + && [sym::cfg_trace, sym::cfg_attr_trace].contains(&name) + { + return; + } + + // FIXME: We should retrieve this information from the attribute parsers instead of from `BUILTIN_ATTRIBUTE_MAP` + let builtin_attr_info = name.and_then(|name| BUILTIN_ATTRIBUTE_MAP.get(&name)); + let builtin_attr_safety = builtin_attr_info.map(|x| x.safety); + + match (builtin_attr_safety, attr_safety) { + // - Unsafe builtin attribute + // - User wrote `#[unsafe(..)]`, which is permitted on any edition + (Some(AttributeSafety::Unsafe { .. }), Safety::Unsafe(..)) => { + // OK + } + + // - Unsafe builtin attribute + // - User did not write `#[unsafe(..)]` + (Some(AttributeSafety::Unsafe { unsafe_since }), Safety::Default) => { + let path_span = attr_path.span; + + // If the `attr_item`'s span is not from a macro, then just suggest + // wrapping it in `unsafe(...)`. Otherwise, we suggest putting the + // `unsafe(`, `)` right after and right before the opening and closing + // square bracket respectively. + let diag_span = attr_span; + + // Attributes can be safe in earlier editions, and become unsafe in later ones. + // + // Use the span of the attribute's name to determine the edition: the span of the + // attribute as a whole may be inaccurate if it was emitted by a macro. + // + // See https://github.com/rust-lang/rust/issues/142182. + let emit_error = match unsafe_since { + None => true, + Some(unsafe_since) => path_span.edition() >= unsafe_since, + }; + + if emit_error { + self.stage.emit_err( + self.sess, + crate::session_diagnostics::UnsafeAttrOutsideUnsafe { + span: path_span, + suggestion: + crate::session_diagnostics::UnsafeAttrOutsideUnsafeSuggestion { + left: diag_span.shrink_to_lo(), + right: diag_span.shrink_to_hi(), + }, + }, + ); + } else { + emit_lint(AttributeLint { + id: target_id, + span: path_span, + kind: AttributeLintKind::UnsafeAttrOutsideUnsafe { + attribute_name_span: path_span, + sugg_spans: (diag_span.shrink_to_lo(), diag_span.shrink_to_hi()), + }, + }) + } + } + + // - Normal builtin attribute + // - Writing `#[unsafe(..)]` is not permitted on normal builtin attributes + (None | Some(AttributeSafety::Normal), Safety::Unsafe(unsafe_span)) => { + self.stage.emit_err( + self.sess, + crate::session_diagnostics::InvalidAttrUnsafe { + span: unsafe_span, + name: attr_path.clone(), + }, + ); + } + + // - Normal builtin attribute + // - No explicit `#[unsafe(..)]` written. + (None | Some(AttributeSafety::Normal), Safety::Default) => { + // OK + } + + ( + Some(AttributeSafety::Unsafe { .. } | AttributeSafety::Normal) | None, + Safety::Safe(..), + ) => { + self.sess.dcx().span_delayed_bug( + attr_span, + "`check_attribute_safety` does not expect `Safety::Safe` on attributes", + ); + } + } + } +} diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 8d783503f7be..f94f0867451f 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -1,6 +1,6 @@ use std::num::IntErrorKind; -use rustc_ast::{self as ast, Path}; +use rustc_ast::{self as ast}; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, @@ -790,7 +790,7 @@ pub(crate) struct InvalidAttrUnsafe { #[primary_span] #[label] pub span: Span, - pub name: Path, + pub name: AttrPath, } #[derive(Diagnostic)] @@ -803,6 +803,15 @@ pub(crate) struct UnsafeAttrOutsideUnsafe { pub suggestion: UnsafeAttrOutsideUnsafeSuggestion, } +#[derive(LintDiagnostic)] +#[diag(attr_parsing_unsafe_attr_outside_unsafe)] +pub(crate) struct UnsafeAttrOutsideUnsafeLint { + #[label] + pub span: Span, + #[subdiagnostic] + pub suggestion: UnsafeAttrOutsideUnsafeSuggestion, +} + #[derive(Subdiagnostic)] #[multipart_suggestion( attr_parsing_unsafe_attr_outside_unsafe_suggestion, diff --git a/compiler/rustc_attr_parsing/src/validate_attr.rs b/compiler/rustc_attr_parsing/src/validate_attr.rs index 4065fe7ce173..9034fe45427f 100644 --- a/compiler/rustc_attr_parsing/src/validate_attr.rs +++ b/compiler/rustc_attr_parsing/src/validate_attr.rs @@ -5,21 +5,21 @@ use std::slice; use rustc_ast::token::Delimiter; use rustc_ast::tokenstream::DelimSpan; use rustc_ast::{ - self as ast, AttrArgs, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, NodeId, - Path, Safety, + self as ast, AttrArgs, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, Safety, }; -use rustc_errors::{Applicability, DiagCtxtHandle, FatalError, PResult}; -use rustc_feature::{AttributeSafety, AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute}; +use rustc_errors::{Applicability, FatalError, PResult}; +use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute}; +use rustc_hir::AttrPath; use rustc_parse::parse_in; use rustc_session::errors::report_lit_error; use rustc_session::lint::BuiltinLintDiag; -use rustc_session::lint::builtin::{ILL_FORMED_ATTRIBUTE_INPUT, UNSAFE_ATTR_OUTSIDE_UNSAFE}; +use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT; use rustc_session::parse::ParseSess; use rustc_span::{Span, Symbol, sym}; use crate::{AttributeParser, Late, session_diagnostics as errors}; -pub fn check_attr(psess: &ParseSess, attr: &Attribute, id: NodeId) { +pub fn check_attr(psess: &ParseSess, attr: &Attribute) { if attr.is_doc_comment() || attr.has_name(sym::cfg_trace) || attr.has_name(sym::cfg_attr_trace) { return; @@ -27,9 +27,6 @@ pub fn check_attr(psess: &ParseSess, attr: &Attribute, id: NodeId) { let builtin_attr_info = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)); - let builtin_attr_safety = builtin_attr_info.map(|x| x.safety); - check_attribute_safety(psess, builtin_attr_safety, attr, id); - // Check input tokens for built-in and key-value attributes. match builtin_attr_info { // `rustc_dummy` doesn't have any restrictions specific to built-in attributes. @@ -150,101 +147,6 @@ fn is_attr_template_compatible(template: &AttributeTemplate, meta: &ast::MetaIte } } -pub fn check_attribute_safety( - psess: &ParseSess, - builtin_attr_safety: Option, - attr: &Attribute, - id: NodeId, -) { - let attr_item = attr.get_normal_item(); - match (builtin_attr_safety, attr_item.unsafety) { - // - Unsafe builtin attribute - // - User wrote `#[unsafe(..)]`, which is permitted on any edition - (Some(AttributeSafety::Unsafe { .. }), Safety::Unsafe(..)) => { - // OK - } - - // - Unsafe builtin attribute - // - User did not write `#[unsafe(..)]` - (Some(AttributeSafety::Unsafe { unsafe_since }), Safety::Default) => { - let path_span = attr_item.path.span; - - // If the `attr_item`'s span is not from a macro, then just suggest - // wrapping it in `unsafe(...)`. Otherwise, we suggest putting the - // `unsafe(`, `)` right after and right before the opening and closing - // square bracket respectively. - let diag_span = attr_item.span(); - - // Attributes can be safe in earlier editions, and become unsafe in later ones. - // - // Use the span of the attribute's name to determine the edition: the span of the - // attribute as a whole may be inaccurate if it was emitted by a macro. - // - // See https://github.com/rust-lang/rust/issues/142182. - let emit_error = match unsafe_since { - None => true, - Some(unsafe_since) => path_span.edition() >= unsafe_since, - }; - - if emit_error { - psess.dcx().emit_err(errors::UnsafeAttrOutsideUnsafe { - span: path_span, - suggestion: errors::UnsafeAttrOutsideUnsafeSuggestion { - left: diag_span.shrink_to_lo(), - right: diag_span.shrink_to_hi(), - }, - }); - } else { - psess.buffer_lint( - UNSAFE_ATTR_OUTSIDE_UNSAFE, - path_span, - id, - BuiltinLintDiag::UnsafeAttrOutsideUnsafe { - attribute_name_span: path_span, - sugg_spans: (diag_span.shrink_to_lo(), diag_span.shrink_to_hi()), - }, - ); - } - } - - // - Normal builtin attribute - // - Writing `#[unsafe(..)]` is not permitted on normal builtin attributes - (None | Some(AttributeSafety::Normal), Safety::Unsafe(unsafe_span)) => { - psess.dcx().emit_err(errors::InvalidAttrUnsafe { - span: unsafe_span, - name: attr_item.path.clone(), - }); - } - - // - Normal builtin attribute - // - No explicit `#[unsafe(..)]` written. - (None | Some(AttributeSafety::Normal), Safety::Default) => { - // OK - } - - ( - Some(AttributeSafety::Unsafe { .. } | AttributeSafety::Normal) | None, - Safety::Safe(..), - ) => { - psess.dcx().span_delayed_bug( - attr_item.span(), - "`check_attribute_safety` does not expect `Safety::Safe` on attributes", - ); - } - } -} - -// Called by `check_builtin_meta_item` and code that manually denies -// `unsafe(...)` in `cfg` -pub fn deny_builtin_meta_unsafety(diag: DiagCtxtHandle<'_>, unsafety: Safety, name: &Path) { - // This only supports denying unsafety right now - making builtin attributes - // support unsafety will requite us to thread the actual `Attribute` through - // for the nice diagnostics. - if let Safety::Unsafe(unsafe_span) = unsafety { - diag.emit_err(errors::InvalidAttrUnsafe { span: unsafe_span, name: name.clone() }); - } -} - pub fn check_builtin_meta_item( psess: &ParseSess, meta: &MetaItem, @@ -258,8 +160,11 @@ pub fn check_builtin_meta_item( emit_malformed_attribute(psess, style, meta.span, name, template); } - if deny_unsafety { - deny_builtin_meta_unsafety(psess.dcx(), meta.unsafety, &meta.path); + if deny_unsafety && let Safety::Unsafe(unsafe_span) = meta.unsafety { + psess.dcx().emit_err(errors::InvalidAttrUnsafe { + span: unsafe_span, + name: AttrPath::from_ast(&meta.path), + }); } } diff --git a/compiler/rustc_builtin_macros/src/cfg.rs b/compiler/rustc_builtin_macros/src/cfg.rs index 5ca4fff94878..b24e3065622d 100644 --- a/compiler/rustc_builtin_macros/src/cfg.rs +++ b/compiler/rustc_builtin_macros/src/cfg.rs @@ -54,6 +54,7 @@ fn parse_cfg(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream) -> Result StripUnconfigured<'a> { /// is in the original source file. Gives a compiler error if the syntax of /// the attribute is incorrect. pub(crate) fn expand_cfg_attr(&self, cfg_attr: &Attribute, recursive: bool) -> Vec { - validate_attr::check_attribute_safety( - &self.sess.psess, - Some(AttributeSafety::Normal), - &cfg_attr, - ast::CRATE_NODE_ID, - ); - // A trace attribute left in AST in place of the original `cfg_attr` attribute. // It can later be used by lints or other diagnostics. let trace_attr = attr_into_trace(cfg_attr.clone(), sym::cfg_attr_trace); @@ -421,13 +412,6 @@ impl<'a> StripUnconfigured<'a> { node: NodeId, emit_errors: ShouldEmit, ) -> EvalConfigResult { - // Unsafety check needs to be done explicitly here because this attribute will be removed before the normal check - deny_builtin_meta_unsafety( - self.sess.dcx(), - attr.get_normal_item().unsafety, - &rustc_ast::Path::from_ident(attr.ident().unwrap()), - ); - let Some(cfg) = AttributeParser::parse_single( self.sess, attr, diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 6d048c120a21..20fb321307ac 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -2158,11 +2158,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { let mut span: Option = None; while let Some(attr) = attrs.next() { rustc_ast_passes::feature_gate::check_attribute(attr, self.cx.sess, features); - validate_attr::check_attr( - &self.cx.sess.psess, - attr, - self.cx.current_expansion.lint_node_id, - ); + validate_attr::check_attr(&self.cx.sess.psess, attr); AttributeParser::parse_limited_all( self.cx.sess, slice::from_ref(attr), diff --git a/compiler/rustc_hir/src/lints.rs b/compiler/rustc_hir/src/lints.rs index 8563937f70d3..a4c60fd2cc1a 100644 --- a/compiler/rustc_hir/src/lints.rs +++ b/compiler/rustc_hir/src/lints.rs @@ -62,4 +62,8 @@ pub enum AttributeLintKind { target: Target, target_span: Span, }, + UnsafeAttrOutsideUnsafe { + attribute_name_span: Span, + sugg_spans: (Span, Span), + }, } diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index bf721154d73b..75e7af4c1173 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -890,10 +890,6 @@ lint_unpredictable_fn_pointer_comparisons = function pointer comparisons do not lint_unqualified_local_imports = `use` of a local item without leading `self::`, `super::`, or `crate::` -lint_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe - .label = usage of unsafe attribute -lint_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)` - lint_unsupported_group = `{$lint_group}` lint group is not supported with ´--force-warn´ lint_untranslatable_diag = diagnostics should be created using translatable messages diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index e376d7d2ab88..87ccd114ee97 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -283,16 +283,6 @@ pub fn decorate_builtin_lint( BuiltinLintDiag::UnusedQualifications { removal_span } => { lints::UnusedQualifications { removal_span }.decorate_lint(diag); } - BuiltinLintDiag::UnsafeAttrOutsideUnsafe { - attribute_name_span, - sugg_spans: (left, right), - } => { - lints::UnsafeAttrOutsideUnsafe { - span: attribute_name_span, - suggestion: lints::UnsafeAttrOutsideUnsafeSuggestion { left, right }, - } - .decorate_lint(diag); - } BuiltinLintDiag::AssociatedConstElidedLifetime { elided, span: lt_span, diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 0687490645d3..6568aa6fd5aa 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2880,27 +2880,6 @@ pub(crate) struct AssociatedConstElidedLifetime { pub lifetimes_in_scope: MultiSpan, } -#[derive(LintDiagnostic)] -#[diag(lint_unsafe_attr_outside_unsafe)] -pub(crate) struct UnsafeAttrOutsideUnsafe { - #[label] - pub span: Span, - #[subdiagnostic] - pub suggestion: UnsafeAttrOutsideUnsafeSuggestion, -} - -#[derive(Subdiagnostic)] -#[multipart_suggestion( - lint_unsafe_attr_outside_unsafe_suggestion, - applicability = "machine-applicable" -)] -pub(crate) struct UnsafeAttrOutsideUnsafeSuggestion { - #[suggestion_part(code = "unsafe(")] - pub left: Span, - #[suggestion_part(code = ")")] - pub right: Span, -} - #[derive(LintDiagnostic)] #[diag(lint_static_mut_refs_lint)] pub(crate) struct RefOfMutStatic<'a> { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 326fdaf9cec9..abdc41eb57c2 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -687,10 +687,6 @@ pub enum BuiltinLintDiag { /// The span of the unnecessarily-qualified path to remove. removal_span: Span, }, - UnsafeAttrOutsideUnsafe { - attribute_name_span: Span, - sugg_spans: (Span, Span), - }, AssociatedConstElidedLifetime { elided: bool, span: Span,