1
Fork 0

Add toggle for parse_meta_item unsafe parsing

This makes it possible for the `unsafe(...)` syntax to only be
valid at the top level, and the `NestedMetaItem`s will automatically
reject `unsafe(...)`.
This commit is contained in:
carbotaniuman 2024-07-09 19:06:49 -05:00
parent d8bc8761a5
commit 49db8a5a99
19 changed files with 228 additions and 98 deletions

View file

@ -365,6 +365,7 @@ parse_inner_doc_comment_not_permitted = expected outer doc comment
.sugg_change_inner_to_outer = to annotate the {$item}, change the doc comment from inner to outer style
parse_invalid_attr_unsafe = `{$name}` is not an unsafe attribute
.label = this is not an unsafe attribute
.suggestion = remove the `unsafe(...)`
.note = extraneous unsafe is not allowed in attributes

View file

@ -3188,6 +3188,7 @@ pub(crate) struct DotDotRangeAttribute {
#[note]
pub struct InvalidAttrUnsafe {
#[primary_span]
#[label]
pub span: Span,
pub name: Path,
}

View file

@ -31,6 +31,12 @@ enum OuterAttributeType {
Attribute,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum AllowLeadingUnsafe {
Yes,
No,
}
impl<'a> Parser<'a> {
/// Parses attributes that appear before an item.
pub(super) fn parse_outer_attributes(&mut self) -> PResult<'a, AttrWrapper> {
@ -332,7 +338,7 @@ impl<'a> Parser<'a> {
/// Parses `cfg_attr(pred, attr_item_list)` where `attr_item_list` is comma-delimited.
pub fn parse_cfg_attr(&mut self) -> PResult<'a, (ast::MetaItem, Vec<(ast::AttrItem, Span)>)> {
let cfg_predicate = self.parse_meta_item()?;
let cfg_predicate = self.parse_meta_item(AllowLeadingUnsafe::No)?;
self.expect(&token::Comma)?;
// Presumably, the majority of the time there will only be one attr.
@ -368,7 +374,10 @@ impl<'a> Parser<'a> {
/// MetaItem = SimplePath ( '=' UNSUFFIXED_LIT | '(' MetaSeq? ')' )? ;
/// MetaSeq = MetaItemInner (',' MetaItemInner)* ','? ;
/// ```
pub fn parse_meta_item(&mut self) -> PResult<'a, ast::MetaItem> {
pub fn parse_meta_item(
&mut self,
unsafe_allowed: AllowLeadingUnsafe,
) -> PResult<'a, ast::MetaItem> {
// We can't use `maybe_whole` here because it would bump in the `None`
// case, which we don't want.
if let token::Interpolated(nt) = &self.token.kind
@ -384,7 +393,11 @@ impl<'a> Parser<'a> {
}
let lo = self.token.span;
let is_unsafe = self.eat_keyword(kw::Unsafe);
let is_unsafe = if unsafe_allowed == AllowLeadingUnsafe::Yes {
self.eat_keyword(kw::Unsafe)
} else {
false
};
let unsafety = if is_unsafe {
let unsafe_span = self.prev_token.span;
self.psess.gated_spans.gate(sym::unsafe_attributes, unsafe_span);
@ -427,7 +440,7 @@ impl<'a> Parser<'a> {
Err(err) => err.cancel(), // we provide a better error below
}
match self.parse_meta_item() {
match self.parse_meta_item(AllowLeadingUnsafe::No) {
Ok(mi) => return Ok(ast::NestedMetaItem::MetaItem(mi)),
Err(err) => err.cancel(), // we provide a better error below
}

View file

@ -163,53 +163,53 @@ pub fn check_attribute_safety(
safety: AttributeSafety,
attr: &Attribute,
) {
if features.unsafe_attributes {
let attr_item = attr.get_normal_item();
if !features.unsafe_attributes {
return;
}
if safety == AttributeSafety::Unsafe {
if let ast::Safety::Default = attr_item.unsafety {
let path_span = attr_item.path.span;
let attr_item = attr.get_normal_item();
// 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 = if attr_item.span().can_be_used_for_suggestions() {
attr_item.span()
} else {
attr.span
.with_lo(attr.span.lo() + BytePos(2))
.with_hi(attr.span.hi() - BytePos(1))
};
if safety == AttributeSafety::Unsafe {
if let ast::Safety::Default = attr_item.unsafety {
let path_span = attr_item.path.span;
if attr.span.at_least_rust_2024() {
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,
ast::CRATE_NODE_ID,
BuiltinLintDiag::UnsafeAttrOutsideUnsafe {
attribute_name_span: path_span,
sugg_spans: (diag_span.shrink_to_lo(), diag_span.shrink_to_hi()),
},
);
}
}
} else {
if let Safety::Unsafe(unsafe_span) = attr_item.unsafety {
psess.dcx().emit_err(errors::InvalidAttrUnsafe {
span: unsafe_span,
name: attr_item.path.clone(),
// 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 = if attr_item.span().can_be_used_for_suggestions() {
attr_item.span()
} else {
attr.span.with_lo(attr.span.lo() + BytePos(2)).with_hi(attr.span.hi() - BytePos(1))
};
if attr.span.at_least_rust_2024() {
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,
ast::CRATE_NODE_ID,
BuiltinLintDiag::UnsafeAttrOutsideUnsafe {
attribute_name_span: path_span,
sugg_spans: (diag_span.shrink_to_lo(), diag_span.shrink_to_hi()),
},
);
}
}
} else {
if let Safety::Unsafe(unsafe_span) = attr_item.unsafety {
psess.dcx().emit_err(errors::InvalidAttrUnsafe {
span: unsafe_span,
name: attr_item.path.clone(),
});
}
}
}