Rollup merge of #124214 - carbotaniuman:parse_unsafe_attrs, r=michaelwoerister
Parse unsafe attributes Initial parse implementation for #123757 This is the initial work to parse unsafe attributes, which is represented as an extra `unsafety` field in `MetaItem` and `AttrItem`. There's two areas in the code where it appears that parsing is done manually and not using the parser stuff, and I'm not sure how I'm supposed to thread the change there.
This commit is contained in:
commit
6e534c73c3
@ -488,6 +488,7 @@ pub struct Crate {
|
||||
/// E.g., `#[test]`, `#[derive(..)]`, `#[rustfmt::skip]` or `#[feature = "foo"]`.
|
||||
#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
|
||||
pub struct MetaItem {
|
||||
pub unsafety: Safety,
|
||||
pub path: Path,
|
||||
pub kind: MetaItemKind,
|
||||
pub span: Span,
|
||||
@ -2823,7 +2824,12 @@ pub struct NormalAttr {
|
||||
impl NormalAttr {
|
||||
pub fn from_ident(ident: Ident) -> Self {
|
||||
Self {
|
||||
item: AttrItem { path: Path::from_ident(ident), args: AttrArgs::Empty, tokens: None },
|
||||
item: AttrItem {
|
||||
unsafety: Safety::Default,
|
||||
path: Path::from_ident(ident),
|
||||
args: AttrArgs::Empty,
|
||||
tokens: None,
|
||||
},
|
||||
tokens: None,
|
||||
}
|
||||
}
|
||||
@ -2831,6 +2837,7 @@ pub fn from_ident(ident: Ident) -> Self {
|
||||
|
||||
#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
|
||||
pub struct AttrItem {
|
||||
pub unsafety: Safety,
|
||||
pub path: Path,
|
||||
pub args: AttrArgs,
|
||||
// Tokens for the meta item, e.g. just the `foo` within `#[foo]` or `#![foo]`.
|
||||
|
@ -1,6 +1,8 @@
|
||||
//! Functions dealing with attributes and meta items.
|
||||
|
||||
use crate::ast::{AttrArgs, AttrArgsEq, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute};
|
||||
use crate::ast::{
|
||||
AttrArgs, AttrArgsEq, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute, Safety,
|
||||
};
|
||||
use crate::ast::{DelimArgs, Expr, ExprKind, LitKind, MetaItemLit};
|
||||
use crate::ast::{MetaItem, MetaItemKind, NestedMetaItem, NormalAttr};
|
||||
use crate::ast::{Path, PathSegment, DUMMY_NODE_ID};
|
||||
@ -238,7 +240,12 @@ fn value_str(&self) -> Option<Symbol> {
|
||||
}
|
||||
|
||||
pub fn meta(&self, span: Span) -> Option<MetaItem> {
|
||||
Some(MetaItem { path: self.path.clone(), kind: self.meta_kind()?, span })
|
||||
Some(MetaItem {
|
||||
unsafety: Safety::Default,
|
||||
path: self.path.clone(),
|
||||
kind: self.meta_kind()?,
|
||||
span,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn meta_kind(&self) -> Option<MetaItemKind> {
|
||||
@ -371,7 +378,10 @@ fn from_tokens<'a, I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem>
|
||||
_ => path.span.hi(),
|
||||
};
|
||||
let span = path.span.with_hi(hi);
|
||||
Some(MetaItem { path, kind, span })
|
||||
// FIXME: This parses `unsafe()` not as unsafe attribute syntax in `MetaItem`,
|
||||
// but as a parenthesized list. This (and likely `MetaItem`) should be changed in
|
||||
// such a way that builtin macros don't accept extraneous `unsafe()`.
|
||||
Some(MetaItem { unsafety: Safety::Default, path, kind, span })
|
||||
}
|
||||
}
|
||||
|
||||
@ -555,11 +565,12 @@ pub fn mk_doc_comment(
|
||||
pub fn mk_attr(
|
||||
g: &AttrIdGenerator,
|
||||
style: AttrStyle,
|
||||
unsafety: Safety,
|
||||
path: Path,
|
||||
args: AttrArgs,
|
||||
span: Span,
|
||||
) -> Attribute {
|
||||
mk_attr_from_item(g, AttrItem { path, args, tokens: None }, None, style, span)
|
||||
mk_attr_from_item(g, AttrItem { unsafety, path, args, tokens: None }, None, style, span)
|
||||
}
|
||||
|
||||
pub fn mk_attr_from_item(
|
||||
@ -577,15 +588,22 @@ pub fn mk_attr_from_item(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mk_attr_word(g: &AttrIdGenerator, style: AttrStyle, name: Symbol, span: Span) -> Attribute {
|
||||
pub fn mk_attr_word(
|
||||
g: &AttrIdGenerator,
|
||||
style: AttrStyle,
|
||||
unsafety: Safety,
|
||||
name: Symbol,
|
||||
span: Span,
|
||||
) -> Attribute {
|
||||
let path = Path::from_ident(Ident::new(name, span));
|
||||
let args = AttrArgs::Empty;
|
||||
mk_attr(g, style, path, args, span)
|
||||
mk_attr(g, style, unsafety, path, args, span)
|
||||
}
|
||||
|
||||
pub fn mk_attr_nested_word(
|
||||
g: &AttrIdGenerator,
|
||||
style: AttrStyle,
|
||||
unsafety: Safety,
|
||||
outer: Symbol,
|
||||
inner: Symbol,
|
||||
span: Span,
|
||||
@ -601,12 +619,13 @@ pub fn mk_attr_nested_word(
|
||||
delim: Delimiter::Parenthesis,
|
||||
tokens: inner_tokens,
|
||||
});
|
||||
mk_attr(g, style, path, attr_args, span)
|
||||
mk_attr(g, style, unsafety, path, attr_args, span)
|
||||
}
|
||||
|
||||
pub fn mk_attr_name_value_str(
|
||||
g: &AttrIdGenerator,
|
||||
style: AttrStyle,
|
||||
unsafety: Safety,
|
||||
name: Symbol,
|
||||
val: Symbol,
|
||||
span: Span,
|
||||
@ -621,7 +640,7 @@ pub fn mk_attr_name_value_str(
|
||||
});
|
||||
let path = Path::from_ident(Ident::new(name, span));
|
||||
let args = AttrArgs::Eq(span, AttrArgsEq::Ast(expr));
|
||||
mk_attr(g, style, path, args, span)
|
||||
mk_attr(g, style, unsafety, path, args, span)
|
||||
}
|
||||
|
||||
pub fn filter_by_name(attrs: &[Attribute], name: Symbol) -> impl Iterator<Item = &Attribute> {
|
||||
|
@ -647,8 +647,10 @@ fn noop_visit_attribute<T: MutVisitor>(attr: &mut Attribute, vis: &mut T) {
|
||||
let Attribute { kind, id: _, style: _, span } = attr;
|
||||
match kind {
|
||||
AttrKind::Normal(normal) => {
|
||||
let NormalAttr { item: AttrItem { path, args, tokens }, tokens: attr_tokens } =
|
||||
&mut **normal;
|
||||
let NormalAttr {
|
||||
item: AttrItem { unsafety: _, path, args, tokens },
|
||||
tokens: attr_tokens,
|
||||
} = &mut **normal;
|
||||
vis.visit_path(path);
|
||||
visit_attr_args(args, vis);
|
||||
visit_lazy_tts(tokens, vis);
|
||||
@ -678,7 +680,7 @@ fn noop_visit_meta_list_item<T: MutVisitor>(li: &mut NestedMetaItem, vis: &mut T
|
||||
}
|
||||
|
||||
fn noop_visit_meta_item<T: MutVisitor>(mi: &mut MetaItem, vis: &mut T) {
|
||||
let MetaItem { path: _, kind, span } = mi;
|
||||
let MetaItem { unsafety: _, path: _, kind, span } = mi;
|
||||
match kind {
|
||||
MetaItemKind::Word => {}
|
||||
MetaItemKind::List(mis) => visit_thin_vec(mis, |mi| vis.visit_meta_list_item(mi)),
|
||||
@ -840,7 +842,7 @@ fn visit_nonterminal<T: MutVisitor>(nt: &mut token::Nonterminal, vis: &mut T) {
|
||||
token::NtTy(ty) => vis.visit_ty(ty),
|
||||
token::NtLiteral(expr) => vis.visit_expr(expr),
|
||||
token::NtMeta(item) => {
|
||||
let AttrItem { path, args, tokens } = item.deref_mut();
|
||||
let AttrItem { unsafety: _, path, args, tokens } = item.deref_mut();
|
||||
vis.visit_path(path);
|
||||
visit_attr_args(args, vis);
|
||||
visit_lazy_tts(tokens, vis);
|
||||
|
@ -1805,6 +1805,7 @@ fn lower_expr_try(&mut self, span: Span, sub_expr: &Expr) -> hir::ExprKind<'hir>
|
||||
let attr = attr::mk_attr_nested_word(
|
||||
&self.tcx.sess.psess.attr_id_generator,
|
||||
AttrStyle::Outer,
|
||||
Safety::Default,
|
||||
sym::allow,
|
||||
sym::unreachable_code,
|
||||
self.lower_span(span),
|
||||
|
@ -905,6 +905,7 @@ fn lower_attr(&self, attr: &Attribute) -> Attribute {
|
||||
let kind = match attr.kind {
|
||||
AttrKind::Normal(ref normal) => AttrKind::Normal(P(NormalAttr {
|
||||
item: AttrItem {
|
||||
unsafety: normal.item.unsafety,
|
||||
path: normal.item.path.clone(),
|
||||
args: self.lower_attr_args(&normal.item.args),
|
||||
tokens: None,
|
||||
|
@ -561,6 +561,7 @@ macro_rules! gate_all {
|
||||
gate_all!(mut_ref, "mutable by-reference bindings are experimental");
|
||||
gate_all!(precise_capturing, "precise captures on `impl Trait` are experimental");
|
||||
gate_all!(global_registration, "global registration is experimental");
|
||||
gate_all!(unsafe_attributes, "`#[unsafe()]` markers for attributes are experimental");
|
||||
|
||||
if !visitor.features.never_patterns {
|
||||
if let Some(spans) = spans.get(&sym::never_patterns) {
|
||||
|
@ -16,7 +16,7 @@
|
||||
use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree};
|
||||
use rustc_ast::util::classify;
|
||||
use rustc_ast::util::comments::{Comment, CommentStyle};
|
||||
use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, BlockCheckMode, PatKind};
|
||||
use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, BlockCheckMode, PatKind, Safety};
|
||||
use rustc_ast::{attr, BindingMode, ByRef, DelimArgs, RangeEnd, RangeSyntax, Term};
|
||||
use rustc_ast::{GenericArg, GenericBound, SelfKind};
|
||||
use rustc_ast::{InlineAsmOperand, InlineAsmRegOrRegClass};
|
||||
@ -249,6 +249,7 @@ pub fn print_crate<'a>(
|
||||
let fake_attr = attr::mk_attr_nested_word(
|
||||
g,
|
||||
ast::AttrStyle::Inner,
|
||||
Safety::Default,
|
||||
sym::feature,
|
||||
sym::prelude_import,
|
||||
DUMMY_SP,
|
||||
@ -259,7 +260,13 @@ pub fn print_crate<'a>(
|
||||
// root, so this is not needed, and actually breaks things.
|
||||
if edition.is_rust_2015() {
|
||||
// `#![no_std]`
|
||||
let fake_attr = attr::mk_attr_word(g, ast::AttrStyle::Inner, sym::no_std, DUMMY_SP);
|
||||
let fake_attr = attr::mk_attr_word(
|
||||
g,
|
||||
ast::AttrStyle::Inner,
|
||||
Safety::Default,
|
||||
sym::no_std,
|
||||
DUMMY_SP,
|
||||
);
|
||||
s.print_attribute(&fake_attr);
|
||||
}
|
||||
}
|
||||
|
@ -110,6 +110,9 @@ builtin_macros_derive_path_args_list = traits in `#[derive(...)]` don't accept a
|
||||
builtin_macros_derive_path_args_value = traits in `#[derive(...)]` don't accept values
|
||||
.suggestion = remove the value
|
||||
|
||||
builtin_macros_derive_unsafe_path = traits in `#[derive(...)]` don't accept `unsafe(...)`
|
||||
.suggestion = remove the `unsafe(...)`
|
||||
|
||||
builtin_macros_env_not_defined = environment variable `{$var}` not defined at compile time
|
||||
.cargo = Cargo sets build script variables at run time. Use `std::env::var({$var_expr})` instead
|
||||
.custom = use `std::env::var({$var_expr})` to read the variable at run time
|
||||
|
@ -17,7 +17,7 @@ pub fn inject(krate: &mut ast::Crate, psess: &ParseSess, attrs: &[String]) {
|
||||
));
|
||||
|
||||
let start_span = parser.token.span;
|
||||
let AttrItem { path, args, tokens: _ } = match parser.parse_attr_item(false) {
|
||||
let AttrItem { unsafety, path, args, tokens: _ } = match parser.parse_attr_item(false) {
|
||||
Ok(ai) => ai,
|
||||
Err(err) => {
|
||||
err.emit();
|
||||
@ -33,6 +33,7 @@ pub fn inject(krate: &mut ast::Crate, psess: &ParseSess, attrs: &[String]) {
|
||||
krate.attrs.push(mk_attr(
|
||||
&psess.attr_id_generator,
|
||||
AttrStyle::Inner,
|
||||
unsafety,
|
||||
path,
|
||||
args,
|
||||
start_span.to(end_span),
|
||||
|
@ -2,7 +2,7 @@
|
||||
use crate::errors;
|
||||
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::{GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, StmtKind};
|
||||
use rustc_ast::{GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, Safety, StmtKind};
|
||||
use rustc_expand::base::{
|
||||
Annotatable, DeriveResolution, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier,
|
||||
};
|
||||
@ -60,6 +60,7 @@ fn expand(
|
||||
// Reject `#[derive(Debug = "value", Debug(abc))]`, but recover the
|
||||
// paths.
|
||||
report_path_args(sess, meta);
|
||||
report_unsafe_args(sess, meta);
|
||||
meta.path.clone()
|
||||
})
|
||||
.map(|path| DeriveResolution {
|
||||
@ -159,3 +160,13 @@ fn report_path_args(sess: &Session, meta: &ast::MetaItem) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn report_unsafe_args(sess: &Session, meta: &ast::MetaItem) {
|
||||
match meta.unsafety {
|
||||
Safety::Unsafe(span) => {
|
||||
sess.dcx().emit_err(errors::DeriveUnsafePath { span });
|
||||
}
|
||||
Safety::Default => {}
|
||||
Safety::Safe(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
@ -295,6 +295,13 @@ pub(crate) struct DerivePathArgsValue {
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_derive_unsafe_path)]
|
||||
pub(crate) struct DeriveUnsafePath {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_no_default_variant)]
|
||||
#[help]
|
||||
|
@ -203,6 +203,7 @@ impl<'a> MutVisitor for EntryPointCleaner<'a> {
|
||||
let allow_dead_code = attr::mk_attr_nested_word(
|
||||
&self.sess.psess.attr_id_generator,
|
||||
ast::AttrStyle::Outer,
|
||||
ast::Safety::Default,
|
||||
sym::allow,
|
||||
sym::dead_code,
|
||||
self.def_site,
|
||||
|
@ -666,7 +666,7 @@ pub fn item_const(
|
||||
// Builds `#[name]`.
|
||||
pub fn attr_word(&self, name: Symbol, span: Span) -> ast::Attribute {
|
||||
let g = &self.sess.psess.attr_id_generator;
|
||||
attr::mk_attr_word(g, ast::AttrStyle::Outer, name, span)
|
||||
attr::mk_attr_word(g, ast::AttrStyle::Outer, ast::Safety::Default, name, span)
|
||||
}
|
||||
|
||||
// Builds `#[name = val]`.
|
||||
@ -674,12 +674,26 @@ pub fn attr_word(&self, name: Symbol, span: Span) -> ast::Attribute {
|
||||
// Note: `span` is used for both the identifier and the value.
|
||||
pub fn attr_name_value_str(&self, name: Symbol, val: Symbol, span: Span) -> ast::Attribute {
|
||||
let g = &self.sess.psess.attr_id_generator;
|
||||
attr::mk_attr_name_value_str(g, ast::AttrStyle::Outer, name, val, span)
|
||||
attr::mk_attr_name_value_str(
|
||||
g,
|
||||
ast::AttrStyle::Outer,
|
||||
ast::Safety::Default,
|
||||
name,
|
||||
val,
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
// Builds `#[outer(inner)]`.
|
||||
pub fn attr_nested_word(&self, outer: Symbol, inner: Symbol, span: Span) -> ast::Attribute {
|
||||
let g = &self.sess.psess.attr_id_generator;
|
||||
attr::mk_attr_nested_word(g, ast::AttrStyle::Outer, outer, inner, span)
|
||||
attr::mk_attr_nested_word(
|
||||
g,
|
||||
ast::AttrStyle::Outer,
|
||||
ast::Safety::Default,
|
||||
outer,
|
||||
inner,
|
||||
span,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -778,7 +778,14 @@ fn expand_invoc(
|
||||
if let SyntaxExtensionKind::Derive(..) = ext {
|
||||
self.gate_proc_macro_input(&item);
|
||||
}
|
||||
let meta = ast::MetaItem { kind: MetaItemKind::Word, span, path };
|
||||
// The `MetaItem` representing the trait to derive can't
|
||||
// have an unsafe around it (as of now).
|
||||
let meta = ast::MetaItem {
|
||||
unsafety: ast::Safety::Default,
|
||||
kind: MetaItemKind::Word,
|
||||
span,
|
||||
path,
|
||||
};
|
||||
let items = match expander.expand(self.cx, span, &meta, item, is_const) {
|
||||
ExpandResult::Ready(items) => items,
|
||||
ExpandResult::Retry(item) => {
|
||||
|
@ -59,6 +59,16 @@ pub enum AttributeType {
|
||||
CrateLevel,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub enum AttributeSafety {
|
||||
/// Normal attribute that does not need `#[unsafe(...)]`
|
||||
Normal,
|
||||
|
||||
/// Unsafe attribute that requires safety obligations
|
||||
/// to be discharged
|
||||
Unsafe,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum AttributeGate {
|
||||
/// Is gated by a given feature gate, reason
|
||||
@ -172,11 +182,23 @@ macro_rules! template {
|
||||
}
|
||||
|
||||
macro_rules! ungated {
|
||||
(unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr $(,)?) => {
|
||||
BuiltinAttribute {
|
||||
name: sym::$attr,
|
||||
encode_cross_crate: $encode_cross_crate,
|
||||
type_: $typ,
|
||||
safety: AttributeSafety::Unsafe,
|
||||
template: $tpl,
|
||||
gate: Ungated,
|
||||
duplicates: $duplicates,
|
||||
}
|
||||
};
|
||||
($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr $(,)?) => {
|
||||
BuiltinAttribute {
|
||||
name: sym::$attr,
|
||||
encode_cross_crate: $encode_cross_crate,
|
||||
type_: $typ,
|
||||
safety: AttributeSafety::Normal,
|
||||
template: $tpl,
|
||||
gate: Ungated,
|
||||
duplicates: $duplicates,
|
||||
@ -185,11 +207,34 @@ macro_rules! ungated {
|
||||
}
|
||||
|
||||
macro_rules! gated {
|
||||
(unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $gate:ident, $msg:expr $(,)?) => {
|
||||
BuiltinAttribute {
|
||||
name: sym::$attr,
|
||||
encode_cross_crate: $encode_cross_crate,
|
||||
type_: $typ,
|
||||
safety: AttributeSafety::Unsafe,
|
||||
template: $tpl,
|
||||
duplicates: $duplicates,
|
||||
gate: Gated(Stability::Unstable, sym::$gate, $msg, cfg_fn!($gate)),
|
||||
}
|
||||
};
|
||||
(unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $msg:expr $(,)?) => {
|
||||
BuiltinAttribute {
|
||||
name: sym::$attr,
|
||||
encode_cross_crate: $encode_cross_crate,
|
||||
type_: $typ,
|
||||
safety: AttributeSafety::Unsafe,
|
||||
template: $tpl,
|
||||
duplicates: $duplicates,
|
||||
gate: Gated(Stability::Unstable, sym::$attr, $msg, cfg_fn!($attr)),
|
||||
}
|
||||
};
|
||||
($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $gate:ident, $msg:expr $(,)?) => {
|
||||
BuiltinAttribute {
|
||||
name: sym::$attr,
|
||||
encode_cross_crate: $encode_cross_crate,
|
||||
type_: $typ,
|
||||
safety: AttributeSafety::Normal,
|
||||
template: $tpl,
|
||||
duplicates: $duplicates,
|
||||
gate: Gated(Stability::Unstable, sym::$gate, $msg, cfg_fn!($gate)),
|
||||
@ -200,6 +245,7 @@ macro_rules! gated {
|
||||
name: sym::$attr,
|
||||
encode_cross_crate: $encode_cross_crate,
|
||||
type_: $typ,
|
||||
safety: AttributeSafety::Normal,
|
||||
template: $tpl,
|
||||
duplicates: $duplicates,
|
||||
gate: Gated(Stability::Unstable, sym::$attr, $msg, cfg_fn!($attr)),
|
||||
@ -228,6 +274,7 @@ macro_rules! rustc_attr {
|
||||
name: sym::$attr,
|
||||
encode_cross_crate: $encode_cross_crate,
|
||||
type_: $typ,
|
||||
safety: AttributeSafety::Normal,
|
||||
template: $tpl,
|
||||
duplicates: $duplicates,
|
||||
gate: Gated(Stability::Unstable, sym::rustc_attrs, $msg, cfg_fn!(rustc_attrs)),
|
||||
@ -258,6 +305,7 @@ pub struct BuiltinAttribute {
|
||||
/// Otherwise, it can only be used in the local crate.
|
||||
pub encode_cross_crate: EncodeCrossCrate,
|
||||
pub type_: AttributeType,
|
||||
pub safety: AttributeSafety,
|
||||
pub template: AttributeTemplate,
|
||||
pub duplicates: AttributeDuplicates,
|
||||
pub gate: AttributeGate,
|
||||
@ -375,9 +423,9 @@ pub struct BuiltinAttribute {
|
||||
),
|
||||
ungated!(no_link, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No),
|
||||
ungated!(repr, Normal, template!(List: "C"), DuplicatesOk, EncodeCrossCrate::No),
|
||||
ungated!(export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No),
|
||||
ungated!(link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No),
|
||||
ungated!(no_mangle, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No),
|
||||
ungated!(unsafe export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No),
|
||||
ungated!(unsafe link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No),
|
||||
ungated!(unsafe no_mangle, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No),
|
||||
ungated!(used, Normal, template!(Word, List: "compiler|linker"), WarnFollowing, EncodeCrossCrate::No),
|
||||
ungated!(link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding, EncodeCrossCrate::Yes),
|
||||
|
||||
@ -486,11 +534,11 @@ pub struct BuiltinAttribute {
|
||||
),
|
||||
|
||||
gated!(
|
||||
ffi_pure, Normal, template!(Word), WarnFollowing,
|
||||
unsafe ffi_pure, Normal, template!(Word), WarnFollowing,
|
||||
EncodeCrossCrate::No, experimental!(ffi_pure)
|
||||
),
|
||||
gated!(
|
||||
ffi_const, Normal, template!(Word), WarnFollowing,
|
||||
unsafe ffi_const, Normal, template!(Word), WarnFollowing,
|
||||
EncodeCrossCrate::No, experimental!(ffi_const)
|
||||
),
|
||||
gated!(
|
||||
@ -850,6 +898,7 @@ pub struct BuiltinAttribute {
|
||||
// FIXME: This can be `true` once we always use `tcx.is_diagnostic_item`.
|
||||
encode_cross_crate: EncodeCrossCrate::Yes,
|
||||
type_: Normal,
|
||||
safety: AttributeSafety::Normal,
|
||||
template: template!(NameValueStr: "name"),
|
||||
duplicates: ErrorFollowing,
|
||||
gate: Gated(
|
||||
@ -1096,6 +1145,10 @@ pub fn is_valid_for_get_attr(name: Symbol) -> bool {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_unsafe_attr(name: Symbol) -> bool {
|
||||
BUILTIN_ATTRIBUTE_MAP.get(&name).is_some_and(|attr| attr.safety == AttributeSafety::Unsafe)
|
||||
}
|
||||
|
||||
pub static BUILTIN_ATTRIBUTE_MAP: LazyLock<FxHashMap<Symbol, &BuiltinAttribute>> =
|
||||
LazyLock::new(|| {
|
||||
let mut map = FxHashMap::default();
|
||||
|
@ -123,8 +123,8 @@ pub fn find_feature_issue(feature: Symbol, issue: GateIssue) -> Option<NonZero<u
|
||||
pub use builtin_attrs::AttributeDuplicates;
|
||||
pub use builtin_attrs::{
|
||||
deprecated_attributes, encode_cross_crate, find_gated_cfg, is_builtin_attr_name,
|
||||
is_valid_for_get_attr, AttributeGate, AttributeTemplate, AttributeType, BuiltinAttribute,
|
||||
GatedCfg, BUILTIN_ATTRIBUTES, BUILTIN_ATTRIBUTE_MAP,
|
||||
is_unsafe_attr, is_valid_for_get_attr, AttributeGate, AttributeTemplate, AttributeType,
|
||||
BuiltinAttribute, GatedCfg, BUILTIN_ATTRIBUTES, BUILTIN_ATTRIBUTE_MAP,
|
||||
};
|
||||
pub use removed::REMOVED_FEATURES;
|
||||
pub use unstable::{Features, INCOMPATIBLE_FEATURES, UNSTABLE_FEATURES};
|
||||
|
@ -620,6 +620,8 @@ pub fn internal(&self, feature: Symbol) -> bool {
|
||||
(unstable, type_changing_struct_update, "1.58.0", Some(86555)),
|
||||
/// Allows unnamed fields of struct and union type
|
||||
(incomplete, unnamed_fields, "1.74.0", Some(49804)),
|
||||
/// Allows unsafe attributes.
|
||||
(unstable, unsafe_attributes, "CURRENT_RUSTC_VERSION", Some(123757)),
|
||||
/// Allows unsafe on extern declarations and safety qualifiers over internal items.
|
||||
(unstable, unsafe_extern_blocks, "CURRENT_RUSTC_VERSION", Some(123743)),
|
||||
/// Allows unsized fn parameters.
|
||||
|
@ -7,7 +7,7 @@
|
||||
use rustc_ast::attr;
|
||||
use rustc_ast::token::{self, Delimiter};
|
||||
use rustc_errors::{codes::*, Diag, PResult};
|
||||
use rustc_span::{sym, BytePos, Span};
|
||||
use rustc_span::{sym, symbol::kw, BytePos, Span};
|
||||
use thin_vec::ThinVec;
|
||||
use tracing::debug;
|
||||
|
||||
@ -252,9 +252,23 @@ pub fn parse_attr_item(&mut self, capture_tokens: bool) -> PResult<'a, ast::Attr
|
||||
maybe_whole!(self, NtMeta, |attr| attr.into_inner());
|
||||
|
||||
let do_parse = |this: &mut Self| {
|
||||
let is_unsafe = this.eat_keyword(kw::Unsafe);
|
||||
let unsafety = if is_unsafe {
|
||||
let unsafe_span = this.prev_token.span;
|
||||
this.psess.gated_spans.gate(sym::unsafe_attributes, unsafe_span);
|
||||
this.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
|
||||
|
||||
ast::Safety::Unsafe(unsafe_span)
|
||||
} else {
|
||||
ast::Safety::Default
|
||||
};
|
||||
|
||||
let path = this.parse_path(PathStyle::Mod)?;
|
||||
let args = this.parse_attr_args()?;
|
||||
Ok(ast::AttrItem { path, args, tokens: None })
|
||||
if is_unsafe {
|
||||
this.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
|
||||
}
|
||||
Ok(ast::AttrItem { unsafety, path, args, tokens: None })
|
||||
};
|
||||
// Attr items don't have attributes
|
||||
if capture_tokens { self.collect_tokens_no_attrs(do_parse) } else { do_parse(self) }
|
||||
@ -375,10 +389,25 @@ pub fn parse_meta_item(&mut self) -> PResult<'a, ast::MetaItem> {
|
||||
}
|
||||
|
||||
let lo = self.token.span;
|
||||
let is_unsafe = self.eat_keyword(kw::Unsafe);
|
||||
let unsafety = if is_unsafe {
|
||||
let unsafe_span = self.prev_token.span;
|
||||
self.psess.gated_spans.gate(sym::unsafe_attributes, unsafe_span);
|
||||
self.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
|
||||
|
||||
ast::Safety::Unsafe(unsafe_span)
|
||||
} else {
|
||||
ast::Safety::Default
|
||||
};
|
||||
|
||||
let path = self.parse_path(PathStyle::Mod)?;
|
||||
let kind = self.parse_meta_item_kind()?;
|
||||
if is_unsafe {
|
||||
self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
|
||||
}
|
||||
let span = lo.to(self.prev_token.span);
|
||||
Ok(ast::MetaItem { path, kind, span })
|
||||
|
||||
Ok(ast::MetaItem { unsafety, path, kind, span })
|
||||
}
|
||||
|
||||
pub(crate) fn parse_meta_item_kind(&mut self) -> PResult<'a, ast::MetaItemKind> {
|
||||
|
@ -42,6 +42,7 @@ pub fn check_attr(psess: &ParseSess, attr: &Attribute) {
|
||||
pub fn parse_meta<'a>(psess: &'a ParseSess, attr: &Attribute) -> PResult<'a, MetaItem> {
|
||||
let item = attr.get_normal_item();
|
||||
Ok(MetaItem {
|
||||
unsafety: item.unsafety,
|
||||
span: attr.span,
|
||||
path: item.path.clone(),
|
||||
kind: match &item.args {
|
||||
|
@ -384,6 +384,10 @@ passes_invalid_attr_at_crate_level =
|
||||
passes_invalid_attr_at_crate_level_item =
|
||||
the inner attribute doesn't annotate this {$kind}
|
||||
|
||||
passes_invalid_attr_unsafe = `{$name}` is not an unsafe attribute
|
||||
.suggestion = remove the `unsafe(...)`
|
||||
.note = extraneous unsafe is not allowed in attributes
|
||||
|
||||
passes_invalid_macro_export_arguments = `{$name}` isn't a valid `#[macro_export]` argument
|
||||
|
||||
passes_invalid_macro_export_arguments_too_many_items = `#[macro_export]` can only take 1 or 0 arguments
|
||||
|
@ -10,7 +10,9 @@
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::StashKey;
|
||||
use rustc_errors::{Applicability, DiagCtxt, IntoDiagArg, MultiSpan};
|
||||
use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
|
||||
use rustc_feature::{
|
||||
is_unsafe_attr, AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP,
|
||||
};
|
||||
use rustc_hir::def_id::LocalModDefId;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::{self as hir};
|
||||
@ -114,6 +116,8 @@ fn check_attributes(
|
||||
let mut seen = FxHashMap::default();
|
||||
let attrs = self.tcx.hir().attrs(hir_id);
|
||||
for attr in attrs {
|
||||
self.check_unsafe_attr(attr);
|
||||
|
||||
match attr.path().as_slice() {
|
||||
[sym::diagnostic, sym::do_not_recommend] => {
|
||||
self.check_do_not_recommend(attr.span, hir_id, target)
|
||||
@ -308,6 +312,21 @@ fn check_do_not_recommend(&self, attr_span: Span, hir_id: HirId, target: Target)
|
||||
true
|
||||
}
|
||||
|
||||
/// Checks if `unsafe()` is applied to an invalid attribute.
|
||||
fn check_unsafe_attr(&self, attr: &Attribute) {
|
||||
if !attr.is_doc_comment() {
|
||||
let attr_item = attr.get_normal_item();
|
||||
if let ast::Safety::Unsafe(unsafe_span) = attr_item.unsafety {
|
||||
if !is_unsafe_attr(attr.name_or_empty()) {
|
||||
self.dcx().emit_err(errors::InvalidAttrUnsafe {
|
||||
span: unsafe_span,
|
||||
name: attr_item.path.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if `#[diagnostic::on_unimplemented]` is applied to a trait definition
|
||||
fn check_diagnostic_on_unimplemented(
|
||||
&self,
|
||||
|
@ -4,7 +4,7 @@
|
||||
};
|
||||
|
||||
use crate::fluent_generated as fluent;
|
||||
use rustc_ast::Label;
|
||||
use rustc_ast::{ast, Label};
|
||||
use rustc_errors::{
|
||||
codes::*, Applicability, Diag, DiagCtxt, DiagSymbolList, Diagnostic, EmissionGuarantee, Level,
|
||||
MultiSpan, SubdiagMessageOp, Subdiagnostic,
|
||||
@ -863,6 +863,15 @@ pub struct InvalidAttrAtCrateLevel {
|
||||
pub item: Option<ItemFollowingInnerAttr>,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_invalid_attr_unsafe)]
|
||||
#[note]
|
||||
pub struct InvalidAttrUnsafe {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub name: ast::Path,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct ItemFollowingInnerAttr {
|
||||
pub span: Span,
|
||||
|
@ -1962,6 +1962,7 @@
|
||||
unreachable_display,
|
||||
unreachable_macro,
|
||||
unrestricted_attribute_tokens,
|
||||
unsafe_attributes,
|
||||
unsafe_block_in_unsafe_fn,
|
||||
unsafe_cell,
|
||||
unsafe_cell_raw_get,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::*;
|
||||
|
||||
use rustc_ast::{MetaItemLit, Path, StrStyle};
|
||||
use rustc_ast::{MetaItemLit, Path, Safety, StrStyle};
|
||||
use rustc_span::create_default_session_globals_then;
|
||||
use rustc_span::symbol::{kw, Ident};
|
||||
use rustc_span::DUMMY_SP;
|
||||
@ -16,6 +16,7 @@ fn name_value_cfg(name: &str, value: &str) -> Cfg {
|
||||
|
||||
fn dummy_meta_item_word(name: &str) -> MetaItem {
|
||||
MetaItem {
|
||||
unsafety: Safety::Default,
|
||||
path: Path::from_ident(Ident::from_str(name)),
|
||||
kind: MetaItemKind::Word,
|
||||
span: DUMMY_SP,
|
||||
@ -25,6 +26,7 @@ fn dummy_meta_item_word(name: &str) -> MetaItem {
|
||||
fn dummy_meta_item_name_value(name: &str, symbol: Symbol, kind: LitKind) -> MetaItem {
|
||||
let lit = MetaItemLit { symbol, suffix: None, kind, span: DUMMY_SP };
|
||||
MetaItem {
|
||||
unsafety: Safety::Default,
|
||||
path: Path::from_ident(Ident::from_str(name)),
|
||||
kind: MetaItemKind::NameValue(lit),
|
||||
span: DUMMY_SP,
|
||||
@ -34,6 +36,7 @@ fn dummy_meta_item_name_value(name: &str, symbol: Symbol, kind: LitKind) -> Meta
|
||||
macro_rules! dummy_meta_item_list {
|
||||
($name:ident, [$($list:ident),* $(,)?]) => {
|
||||
MetaItem {
|
||||
unsafety: Safety::Default,
|
||||
path: Path::from_ident(Ident::from_str(stringify!($name))),
|
||||
kind: MetaItemKind::List(thin_vec![
|
||||
$(
|
||||
@ -48,6 +51,7 @@ macro_rules! dummy_meta_item_list {
|
||||
|
||||
($name:ident, [$($list:expr),* $(,)?]) => {
|
||||
MetaItem {
|
||||
unsafety: Safety::Default,
|
||||
path: Path::from_ident(Ident::from_str(stringify!($name))),
|
||||
kind: MetaItemKind::List(thin_vec![
|
||||
$(
|
||||
|
7
tests/ui/attributes/unsafe/cfg-unsafe-attributes.rs
Normal file
7
tests/ui/attributes/unsafe/cfg-unsafe-attributes.rs
Normal file
@ -0,0 +1,7 @@
|
||||
//@ build-pass
|
||||
#![feature(unsafe_attributes)]
|
||||
|
||||
#[cfg_attr(all(), unsafe(no_mangle))]
|
||||
fn a() {}
|
||||
|
||||
fn main() {}
|
6
tests/ui/attributes/unsafe/derive-unsafe-attributes.rs
Normal file
6
tests/ui/attributes/unsafe/derive-unsafe-attributes.rs
Normal file
@ -0,0 +1,6 @@
|
||||
#![feature(unsafe_attributes)]
|
||||
|
||||
#[derive(unsafe(Debug))] //~ ERROR: traits in `#[derive(...)]` don't accept `unsafe(...)`
|
||||
struct Foo;
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,8 @@
|
||||
error: traits in `#[derive(...)]` don't accept `unsafe(...)`
|
||||
--> $DIR/derive-unsafe-attributes.rs:3:10
|
||||
|
|
||||
LL | #[derive(unsafe(Debug))]
|
||||
| ^^^^^^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
9
tests/ui/attributes/unsafe/double-unsafe-attributes.rs
Normal file
9
tests/ui/attributes/unsafe/double-unsafe-attributes.rs
Normal file
@ -0,0 +1,9 @@
|
||||
#![feature(unsafe_attributes)]
|
||||
|
||||
#[unsafe(unsafe(no_mangle))]
|
||||
//~^ ERROR expected identifier, found keyword `unsafe`
|
||||
//~| ERROR cannot find attribute `r#unsafe` in this scope
|
||||
//~| ERROR `r#unsafe` is not an unsafe attribute
|
||||
fn a() {}
|
||||
|
||||
fn main() {}
|
27
tests/ui/attributes/unsafe/double-unsafe-attributes.stderr
Normal file
27
tests/ui/attributes/unsafe/double-unsafe-attributes.stderr
Normal file
@ -0,0 +1,27 @@
|
||||
error: expected identifier, found keyword `unsafe`
|
||||
--> $DIR/double-unsafe-attributes.rs:3:10
|
||||
|
|
||||
LL | #[unsafe(unsafe(no_mangle))]
|
||||
| ^^^^^^ expected identifier, found keyword
|
||||
|
|
||||
help: escape `unsafe` to use it as an identifier
|
||||
|
|
||||
LL | #[unsafe(r#unsafe(no_mangle))]
|
||||
| ++
|
||||
|
||||
error: cannot find attribute `r#unsafe` in this scope
|
||||
--> $DIR/double-unsafe-attributes.rs:3:10
|
||||
|
|
||||
LL | #[unsafe(unsafe(no_mangle))]
|
||||
| ^^^^^^
|
||||
|
||||
error: `r#unsafe` is not an unsafe attribute
|
||||
--> $DIR/double-unsafe-attributes.rs:3:3
|
||||
|
|
||||
LL | #[unsafe(unsafe(no_mangle))]
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: extraneous unsafe is not allowed in attributes
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
7
tests/ui/attributes/unsafe/unsafe-attributes.rs
Normal file
7
tests/ui/attributes/unsafe/unsafe-attributes.rs
Normal file
@ -0,0 +1,7 @@
|
||||
//@ build-pass
|
||||
#![feature(unsafe_attributes)]
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
fn a() {}
|
||||
|
||||
fn main() {}
|
6
tests/ui/attributes/unsafe/unsafe-safe-attribute.rs
Normal file
6
tests/ui/attributes/unsafe/unsafe-safe-attribute.rs
Normal file
@ -0,0 +1,6 @@
|
||||
#![feature(unsafe_attributes)]
|
||||
|
||||
#[unsafe(repr(C))] //~ ERROR: is not an unsafe attribute
|
||||
struct Foo {}
|
||||
|
||||
fn main() {}
|
10
tests/ui/attributes/unsafe/unsafe-safe-attribute.stderr
Normal file
10
tests/ui/attributes/unsafe/unsafe-safe-attribute.stderr
Normal file
@ -0,0 +1,10 @@
|
||||
error: `repr` is not an unsafe attribute
|
||||
--> $DIR/unsafe-safe-attribute.rs:3:3
|
||||
|
|
||||
LL | #[unsafe(repr(C))]
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: extraneous unsafe is not allowed in attributes
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
@ -0,0 +1,8 @@
|
||||
#![feature(unsafe_attributes)]
|
||||
|
||||
#[unsafe(diagnostic::on_unimplemented( //~ ERROR: is not an unsafe attribute
|
||||
message = "testing",
|
||||
))]
|
||||
trait Foo {}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,10 @@
|
||||
error: `diagnostic::on_unimplemented` is not an unsafe attribute
|
||||
--> $DIR/unsafe-safe-attribute_diagnostic.rs:3:3
|
||||
|
|
||||
LL | #[unsafe(diagnostic::on_unimplemented(
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: extraneous unsafe is not allowed in attributes
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
8
tests/ui/feature-gates/feature-gate-unsafe-attributes.rs
Normal file
8
tests/ui/feature-gates/feature-gate-unsafe-attributes.rs
Normal file
@ -0,0 +1,8 @@
|
||||
#[unsafe(no_mangle)] //~ ERROR [E0658]
|
||||
extern "C" fn foo() {
|
||||
|
||||
}
|
||||
|
||||
fn main() {
|
||||
foo();
|
||||
}
|
13
tests/ui/feature-gates/feature-gate-unsafe-attributes.stderr
Normal file
13
tests/ui/feature-gates/feature-gate-unsafe-attributes.stderr
Normal file
@ -0,0 +1,13 @@
|
||||
error[E0658]: `#[unsafe()]` markers for attributes are experimental
|
||||
--> $DIR/feature-gate-unsafe-attributes.rs:1:3
|
||||
|
|
||||
LL | #[unsafe(no_mangle)]
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: see issue #123757 <https://github.com/rust-lang/rust/issues/123757> for more information
|
||||
= help: add `#![feature(unsafe_attributes)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
Loading…
Reference in New Issue
Block a user