Auto merge of #127543 - carbotaniuman:more_unsafe_attr_verification, r=estebank,traviscross
More unsafe attr verification This code denies unsafe on attributes such as `#[test]` and `#[ignore]`, while also changing the `MetaItem` parsing so `unsafe` in args like `#[allow(unsafe(dead_code))]` is not accidentally allowed. Tracking: - https://github.com/rust-lang/rust/issues/123757
This commit is contained in:
commit
c0e32983f5
22 changed files with 471 additions and 92 deletions
|
@ -115,9 +115,6 @@ 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
|
builtin_macros_derive_path_args_value = traits in `#[derive(...)]` don't accept values
|
||||||
.suggestion = remove the value
|
.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
|
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
|
.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
|
.custom = use `std::env::var({$var_expr})` to read the variable at run time
|
||||||
|
|
|
@ -6,6 +6,7 @@ use rustc_ast::token;
|
||||||
use rustc_ast::tokenstream::TokenStream;
|
use rustc_ast::tokenstream::TokenStream;
|
||||||
use rustc_errors::PResult;
|
use rustc_errors::PResult;
|
||||||
use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult};
|
use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult};
|
||||||
|
use rustc_parse::parser::attr::AllowLeadingUnsafe;
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
use {rustc_ast as ast, rustc_attr as attr};
|
use {rustc_ast as ast, rustc_attr as attr};
|
||||||
|
|
||||||
|
@ -42,7 +43,7 @@ fn parse_cfg<'a>(cx: &ExtCtxt<'a>, span: Span, tts: TokenStream) -> PResult<'a,
|
||||||
return Err(cx.dcx().create_err(errors::RequiresCfgPattern { span }));
|
return Err(cx.dcx().create_err(errors::RequiresCfgPattern { span }));
|
||||||
}
|
}
|
||||||
|
|
||||||
let cfg = p.parse_meta_item()?;
|
let cfg = p.parse_meta_item(AllowLeadingUnsafe::Yes)?;
|
||||||
|
|
||||||
let _ = p.eat(&token::Comma);
|
let _ = p.eat(&token::Comma);
|
||||||
|
|
||||||
|
|
|
@ -47,11 +47,13 @@ impl MultiItemModifier for Expander {
|
||||||
) -> ExpandResult<Vec<Annotatable>, Annotatable> {
|
) -> ExpandResult<Vec<Annotatable>, Annotatable> {
|
||||||
let template = AttributeTemplate { list: Some("path"), ..Default::default() };
|
let template = AttributeTemplate { list: Some("path"), ..Default::default() };
|
||||||
validate_attr::check_builtin_meta_item(
|
validate_attr::check_builtin_meta_item(
|
||||||
|
&ecx.ecfg.features,
|
||||||
&ecx.sess.psess,
|
&ecx.sess.psess,
|
||||||
meta_item,
|
meta_item,
|
||||||
ast::AttrStyle::Outer,
|
ast::AttrStyle::Outer,
|
||||||
sym::cfg_accessible,
|
sym::cfg_accessible,
|
||||||
template,
|
template,
|
||||||
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
let Some(path) = validate_input(ecx, meta_item) else {
|
let Some(path) = validate_input(ecx, meta_item) else {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use rustc_ast as ast;
|
use rustc_ast as ast;
|
||||||
use rustc_ast::{GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, Safety, StmtKind};
|
use rustc_ast::{GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, StmtKind};
|
||||||
use rustc_expand::base::{
|
use rustc_expand::base::{
|
||||||
Annotatable, DeriveResolution, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier,
|
Annotatable, DeriveResolution, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier,
|
||||||
};
|
};
|
||||||
|
@ -38,11 +38,13 @@ impl MultiItemModifier for Expander {
|
||||||
let template =
|
let template =
|
||||||
AttributeTemplate { list: Some("Trait1, Trait2, ..."), ..Default::default() };
|
AttributeTemplate { list: Some("Trait1, Trait2, ..."), ..Default::default() };
|
||||||
validate_attr::check_builtin_meta_item(
|
validate_attr::check_builtin_meta_item(
|
||||||
|
features,
|
||||||
&sess.psess,
|
&sess.psess,
|
||||||
meta_item,
|
meta_item,
|
||||||
ast::AttrStyle::Outer,
|
ast::AttrStyle::Outer,
|
||||||
sym::derive,
|
sym::derive,
|
||||||
template,
|
template,
|
||||||
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut resolutions = match &meta_item.kind {
|
let mut resolutions = match &meta_item.kind {
|
||||||
|
@ -60,7 +62,6 @@ impl MultiItemModifier for Expander {
|
||||||
// Reject `#[derive(Debug = "value", Debug(abc))]`, but recover the
|
// Reject `#[derive(Debug = "value", Debug(abc))]`, but recover the
|
||||||
// paths.
|
// paths.
|
||||||
report_path_args(sess, meta);
|
report_path_args(sess, meta);
|
||||||
report_unsafe_args(sess, meta);
|
|
||||||
meta.path.clone()
|
meta.path.clone()
|
||||||
})
|
})
|
||||||
.map(|path| DeriveResolution {
|
.map(|path| DeriveResolution {
|
||||||
|
@ -160,13 +161,3 @@ 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!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -297,13 +297,6 @@ pub(crate) struct DerivePathArgsValue {
|
||||||
pub(crate) span: Span,
|
pub(crate) span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Diagnostic)]
|
|
||||||
#[diag(builtin_macros_derive_unsafe_path)]
|
|
||||||
pub(crate) struct DeriveUnsafePath {
|
|
||||||
#[primary_span]
|
|
||||||
pub(crate) span: Span,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Diagnostic)]
|
#[derive(Diagnostic)]
|
||||||
#[diag(builtin_macros_no_default_variant)]
|
#[diag(builtin_macros_no_default_variant)]
|
||||||
#[help]
|
#[help]
|
||||||
|
|
|
@ -17,11 +17,13 @@ pub(crate) fn check_builtin_macro_attribute(ecx: &ExtCtxt<'_>, meta_item: &MetaI
|
||||||
// All the built-in macro attributes are "words" at the moment.
|
// All the built-in macro attributes are "words" at the moment.
|
||||||
let template = AttributeTemplate { word: true, ..Default::default() };
|
let template = AttributeTemplate { word: true, ..Default::default() };
|
||||||
validate_attr::check_builtin_meta_item(
|
validate_attr::check_builtin_meta_item(
|
||||||
|
&ecx.ecfg.features,
|
||||||
&ecx.sess.psess,
|
&ecx.sess.psess,
|
||||||
meta_item,
|
meta_item,
|
||||||
AttrStyle::Outer,
|
AttrStyle::Outer,
|
||||||
name,
|
name,
|
||||||
template,
|
template,
|
||||||
|
true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,9 @@ use rustc_ast::tokenstream::{
|
||||||
use rustc_ast::{self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, NodeId};
|
use rustc_ast::{self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, NodeId};
|
||||||
use rustc_attr as attr;
|
use rustc_attr as attr;
|
||||||
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
|
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
|
||||||
use rustc_feature::{Features, ACCEPTED_FEATURES, REMOVED_FEATURES, UNSTABLE_FEATURES};
|
use rustc_feature::{
|
||||||
|
AttributeSafety, Features, ACCEPTED_FEATURES, REMOVED_FEATURES, UNSTABLE_FEATURES,
|
||||||
|
};
|
||||||
use rustc_lint_defs::BuiltinLintDiag;
|
use rustc_lint_defs::BuiltinLintDiag;
|
||||||
use rustc_parse::validate_attr;
|
use rustc_parse::validate_attr;
|
||||||
use rustc_session::parse::feature_err;
|
use rustc_session::parse::feature_err;
|
||||||
|
@ -263,6 +265,13 @@ impl<'a> StripUnconfigured<'a> {
|
||||||
/// is in the original source file. Gives a compiler error if the syntax of
|
/// is in the original source file. Gives a compiler error if the syntax of
|
||||||
/// the attribute is incorrect.
|
/// the attribute is incorrect.
|
||||||
pub(crate) fn expand_cfg_attr(&self, cfg_attr: &Attribute, recursive: bool) -> Vec<Attribute> {
|
pub(crate) fn expand_cfg_attr(&self, cfg_attr: &Attribute, recursive: bool) -> Vec<Attribute> {
|
||||||
|
validate_attr::check_attribute_safety(
|
||||||
|
self.features.unwrap_or(&Features::default()),
|
||||||
|
&self.sess.psess,
|
||||||
|
AttributeSafety::Normal,
|
||||||
|
&cfg_attr,
|
||||||
|
);
|
||||||
|
|
||||||
let Some((cfg_predicate, expanded_attrs)) =
|
let Some((cfg_predicate, expanded_attrs)) =
|
||||||
rustc_parse::parse_cfg_attr(cfg_attr, &self.sess.psess)
|
rustc_parse::parse_cfg_attr(cfg_attr, &self.sess.psess)
|
||||||
else {
|
else {
|
||||||
|
@ -385,6 +394,13 @@ impl<'a> StripUnconfigured<'a> {
|
||||||
return (true, None);
|
return (true, None);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
validate_attr::deny_builtin_meta_unsafety(
|
||||||
|
self.features.unwrap_or(&Features::default()),
|
||||||
|
&self.sess.psess,
|
||||||
|
&meta_item,
|
||||||
|
);
|
||||||
|
|
||||||
(
|
(
|
||||||
parse_cfg(&meta_item, self.sess).map_or(true, |meta_item| {
|
parse_cfg(&meta_item, self.sess).map_or(true, |meta_item| {
|
||||||
attr::cfg_matches(meta_item, &self.sess, self.lint_node_id, self.features)
|
attr::cfg_matches(meta_item, &self.sess, self.lint_node_id, self.features)
|
||||||
|
|
|
@ -15,6 +15,7 @@ use rustc_middle::ty;
|
||||||
use rustc_middle::ty::CurrentGcx;
|
use rustc_middle::ty::CurrentGcx;
|
||||||
use rustc_middle::util::Providers;
|
use rustc_middle::util::Providers;
|
||||||
use rustc_parse::new_parser_from_source_str;
|
use rustc_parse::new_parser_from_source_str;
|
||||||
|
use rustc_parse::parser::attr::AllowLeadingUnsafe;
|
||||||
use rustc_query_impl::QueryCtxt;
|
use rustc_query_impl::QueryCtxt;
|
||||||
use rustc_query_system::query::print_query_stack;
|
use rustc_query_system::query::print_query_stack;
|
||||||
use rustc_session::config::{self, Cfg, CheckCfg, ExpectedValues, Input, OutFileName};
|
use rustc_session::config::{self, Cfg, CheckCfg, ExpectedValues, Input, OutFileName};
|
||||||
|
@ -67,7 +68,7 @@ pub(crate) fn parse_cfg(dcx: DiagCtxtHandle<'_>, cfgs: Vec<String>) -> Cfg {
|
||||||
}
|
}
|
||||||
|
|
||||||
match new_parser_from_source_str(&psess, filename, s.to_string()) {
|
match new_parser_from_source_str(&psess, filename, s.to_string()) {
|
||||||
Ok(mut parser) => match parser.parse_meta_item() {
|
Ok(mut parser) => match parser.parse_meta_item(AllowLeadingUnsafe::No) {
|
||||||
Ok(meta_item) if parser.token == token::Eof => {
|
Ok(meta_item) if parser.token == token::Eof => {
|
||||||
if meta_item.path.segments.len() != 1 {
|
if meta_item.path.segments.len() != 1 {
|
||||||
error!("argument key must be an identifier");
|
error!("argument key must be an identifier");
|
||||||
|
@ -173,7 +174,7 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec<String>) -> Ch
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let meta_item = match parser.parse_meta_item() {
|
let meta_item = match parser.parse_meta_item(AllowLeadingUnsafe::Yes) {
|
||||||
Ok(meta_item) if parser.token == token::Eof => meta_item,
|
Ok(meta_item) if parser.token == token::Eof => meta_item,
|
||||||
Ok(..) => expected_error(),
|
Ok(..) => expected_error(),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
|
|
@ -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
|
.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
|
parse_invalid_attr_unsafe = `{$name}` is not an unsafe attribute
|
||||||
|
.label = this is not an unsafe attribute
|
||||||
.suggestion = remove the `unsafe(...)`
|
.suggestion = remove the `unsafe(...)`
|
||||||
.note = extraneous unsafe is not allowed in attributes
|
.note = extraneous unsafe is not allowed in attributes
|
||||||
|
|
||||||
|
|
|
@ -3183,6 +3183,7 @@ pub(crate) struct DotDotRangeAttribute {
|
||||||
#[note]
|
#[note]
|
||||||
pub struct InvalidAttrUnsafe {
|
pub struct InvalidAttrUnsafe {
|
||||||
#[primary_span]
|
#[primary_span]
|
||||||
|
#[label]
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
pub name: Path,
|
pub name: Path,
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,12 @@ enum OuterAttributeType {
|
||||||
Attribute,
|
Attribute,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum AllowLeadingUnsafe {
|
||||||
|
Yes,
|
||||||
|
No,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> Parser<'a> {
|
impl<'a> Parser<'a> {
|
||||||
/// Parses attributes that appear before an item.
|
/// Parses attributes that appear before an item.
|
||||||
pub(super) fn parse_outer_attributes(&mut self) -> PResult<'a, AttrWrapper> {
|
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.
|
/// 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)>)> {
|
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)?;
|
self.expect(&token::Comma)?;
|
||||||
|
|
||||||
// Presumably, the majority of the time there will only be one attr.
|
// 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? ')' )? ;
|
/// MetaItem = SimplePath ( '=' UNSUFFIXED_LIT | '(' MetaSeq? ')' )? ;
|
||||||
/// MetaSeq = MetaItemInner (',' MetaItemInner)* ','? ;
|
/// 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`
|
// We can't use `maybe_whole` here because it would bump in the `None`
|
||||||
// case, which we don't want.
|
// case, which we don't want.
|
||||||
if let token::Interpolated(nt) = &self.token.kind
|
if let token::Interpolated(nt) = &self.token.kind
|
||||||
|
@ -384,7 +393,11 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let lo = self.token.span;
|
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 unsafety = if is_unsafe {
|
||||||
let unsafe_span = self.prev_token.span;
|
let unsafe_span = self.prev_token.span;
|
||||||
self.psess.gated_spans.gate(sym::unsafe_attributes, unsafe_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
|
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)),
|
Ok(mi) => return Ok(ast::NestedMetaItem::MetaItem(mi)),
|
||||||
Err(err) => err.cancel(), // we provide a better error below
|
Err(err) => err.cancel(), // we provide a better error below
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,67 +26,26 @@ pub fn check_attr(features: &Features, psess: &ParseSess, attr: &Attribute) {
|
||||||
let attr_info = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name));
|
let attr_info = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name));
|
||||||
let attr_item = attr.get_normal_item();
|
let attr_item = attr.get_normal_item();
|
||||||
|
|
||||||
let is_unsafe_attr = attr_info.is_some_and(|attr| attr.safety == AttributeSafety::Unsafe);
|
// All non-builtin attributes are considered safe
|
||||||
|
let safety = attr_info.map(|x| x.safety).unwrap_or(AttributeSafety::Normal);
|
||||||
if features.unsafe_attributes {
|
check_attribute_safety(features, psess, safety, attr);
|
||||||
if is_unsafe_attr {
|
|
||||||
if let ast::Safety::Default = attr_item.unsafety {
|
|
||||||
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 = 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(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check input tokens for built-in and key-value attributes.
|
// Check input tokens for built-in and key-value attributes.
|
||||||
match attr_info {
|
match attr_info {
|
||||||
// `rustc_dummy` doesn't have any restrictions specific to built-in attributes.
|
// `rustc_dummy` doesn't have any restrictions specific to built-in attributes.
|
||||||
Some(BuiltinAttribute { name, template, .. }) if *name != sym::rustc_dummy => {
|
Some(BuiltinAttribute { name, template, .. }) if *name != sym::rustc_dummy => {
|
||||||
match parse_meta(psess, attr) {
|
match parse_meta(psess, attr) {
|
||||||
Ok(meta) => check_builtin_meta_item(psess, &meta, attr.style, *name, *template),
|
// Don't check safety again, we just did that
|
||||||
|
Ok(meta) => check_builtin_meta_item(
|
||||||
|
features, psess, &meta, attr.style, *name, *template, false,
|
||||||
|
),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
err.emit();
|
err.emit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ if let AttrArgs::Eq(..) = attr_item.args => {
|
_ => {
|
||||||
|
if let AttrArgs::Eq(..) = attr_item.args {
|
||||||
// All key-value attributes are restricted to meta-item syntax.
|
// All key-value attributes are restricted to meta-item syntax.
|
||||||
match parse_meta(psess, attr) {
|
match parse_meta(psess, attr) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
|
@ -95,7 +54,7 @@ pub fn check_attr(features: &Features, psess: &ParseSess, attr: &Attribute) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,12 +157,85 @@ fn is_attr_template_compatible(template: &AttributeTemplate, meta: &ast::MetaIte
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn check_attribute_safety(
|
||||||
|
features: &Features,
|
||||||
|
psess: &ParseSess,
|
||||||
|
safety: AttributeSafety,
|
||||||
|
attr: &Attribute,
|
||||||
|
) {
|
||||||
|
if !features.unsafe_attributes {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let attr_item = attr.get_normal_item();
|
||||||
|
|
||||||
|
if safety == AttributeSafety::Unsafe {
|
||||||
|
if let ast::Safety::Default = attr_item.unsafety {
|
||||||
|
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 = 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(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called by `check_builtin_meta_item` and code that manually denies
|
||||||
|
// `unsafe(...)` in `cfg`
|
||||||
|
pub fn deny_builtin_meta_unsafety(features: &Features, psess: &ParseSess, meta: &MetaItem) {
|
||||||
|
// 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 features.unsafe_attributes {
|
||||||
|
if let Safety::Unsafe(unsafe_span) = meta.unsafety {
|
||||||
|
psess
|
||||||
|
.dcx()
|
||||||
|
.emit_err(errors::InvalidAttrUnsafe { span: unsafe_span, name: meta.path.clone() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn check_builtin_meta_item(
|
pub fn check_builtin_meta_item(
|
||||||
|
features: &Features,
|
||||||
psess: &ParseSess,
|
psess: &ParseSess,
|
||||||
meta: &MetaItem,
|
meta: &MetaItem,
|
||||||
style: ast::AttrStyle,
|
style: ast::AttrStyle,
|
||||||
name: Symbol,
|
name: Symbol,
|
||||||
template: AttributeTemplate,
|
template: AttributeTemplate,
|
||||||
|
deny_unsafety: bool,
|
||||||
) {
|
) {
|
||||||
// Some special attributes like `cfg` must be checked
|
// Some special attributes like `cfg` must be checked
|
||||||
// before the generic check, so we skip them here.
|
// before the generic check, so we skip them here.
|
||||||
|
@ -212,6 +244,10 @@ pub fn check_builtin_meta_item(
|
||||||
if !should_skip(name) && !is_attr_template_compatible(&template, &meta.kind) {
|
if !should_skip(name) && !is_attr_template_compatible(&template, &meta.kind) {
|
||||||
emit_malformed_attribute(psess, style, meta.span, name, template);
|
emit_malformed_attribute(psess, style, meta.span, name, template);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if deny_unsafety {
|
||||||
|
deny_builtin_meta_unsafety(features, psess, meta);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_malformed_attribute(
|
fn emit_malformed_attribute(
|
||||||
|
|
|
@ -1,6 +1,15 @@
|
||||||
#![feature(unsafe_attributes)]
|
#![feature(unsafe_attributes)]
|
||||||
|
|
||||||
#[derive(unsafe(Debug))] //~ ERROR: traits in `#[derive(...)]` don't accept `unsafe(...)`
|
#[derive(unsafe(Debug))]
|
||||||
|
//~^ ERROR: expected identifier, found keyword `unsafe`
|
||||||
|
//~| ERROR: traits in `#[derive(...)]` don't accept arguments
|
||||||
|
//~| ERROR: expected identifier, found keyword `unsafe`
|
||||||
|
//~| ERROR: expected identifier, found keyword `unsafe`
|
||||||
|
//~| ERROR: cannot find derive macro `r#unsafe` in this scope
|
||||||
|
//~| ERROR: cannot find derive macro `r#unsafe` in this scope
|
||||||
struct Foo;
|
struct Foo;
|
||||||
|
|
||||||
|
#[unsafe(derive(Debug))] //~ ERROR: is not an unsafe attribute
|
||||||
|
struct Bar;
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -1,8 +1,65 @@
|
||||||
error: traits in `#[derive(...)]` don't accept `unsafe(...)`
|
error: expected identifier, found keyword `unsafe`
|
||||||
|
--> $DIR/derive-unsafe-attributes.rs:3:10
|
||||||
|
|
|
||||||
|
LL | #[derive(unsafe(Debug))]
|
||||||
|
| ^^^^^^ expected identifier, found keyword
|
||||||
|
|
|
||||||
|
help: escape `unsafe` to use it as an identifier
|
||||||
|
|
|
||||||
|
LL | #[derive(r#unsafe(Debug))]
|
||||||
|
| ++
|
||||||
|
|
||||||
|
error: traits in `#[derive(...)]` don't accept arguments
|
||||||
|
--> $DIR/derive-unsafe-attributes.rs:3:16
|
||||||
|
|
|
||||||
|
LL | #[derive(unsafe(Debug))]
|
||||||
|
| ^^^^^^^ help: remove the arguments
|
||||||
|
|
||||||
|
error: `derive` is not an unsafe attribute
|
||||||
|
--> $DIR/derive-unsafe-attributes.rs:12:3
|
||||||
|
|
|
||||||
|
LL | #[unsafe(derive(Debug))]
|
||||||
|
| ^^^^^^ this is not an unsafe attribute
|
||||||
|
|
|
||||||
|
= note: extraneous unsafe is not allowed in attributes
|
||||||
|
|
||||||
|
error: expected identifier, found keyword `unsafe`
|
||||||
|
--> $DIR/derive-unsafe-attributes.rs:3:10
|
||||||
|
|
|
||||||
|
LL | #[derive(unsafe(Debug))]
|
||||||
|
| ^^^^^^ expected identifier, found keyword
|
||||||
|
|
|
||||||
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
help: escape `unsafe` to use it as an identifier
|
||||||
|
|
|
||||||
|
LL | #[derive(r#unsafe(Debug))]
|
||||||
|
| ++
|
||||||
|
|
||||||
|
error: expected identifier, found keyword `unsafe`
|
||||||
|
--> $DIR/derive-unsafe-attributes.rs:3:10
|
||||||
|
|
|
||||||
|
LL | #[derive(unsafe(Debug))]
|
||||||
|
| ^^^^^^ expected identifier, found keyword
|
||||||
|
|
|
||||||
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
help: escape `unsafe` to use it as an identifier
|
||||||
|
|
|
||||||
|
LL | #[derive(r#unsafe(Debug))]
|
||||||
|
| ++
|
||||||
|
|
||||||
|
error: cannot find derive macro `r#unsafe` in this scope
|
||||||
--> $DIR/derive-unsafe-attributes.rs:3:10
|
--> $DIR/derive-unsafe-attributes.rs:3:10
|
||||||
|
|
|
|
||||||
LL | #[derive(unsafe(Debug))]
|
LL | #[derive(unsafe(Debug))]
|
||||||
| ^^^^^^
|
| ^^^^^^
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: cannot find derive macro `r#unsafe` in this scope
|
||||||
|
--> $DIR/derive-unsafe-attributes.rs:3:10
|
||||||
|
|
|
||||||
|
LL | #[derive(unsafe(Debug))]
|
||||||
|
| ^^^^^^
|
||||||
|
|
|
||||||
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
|
error: aborting due to 7 previous errors
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ error: `r#unsafe` is not an unsafe attribute
|
||||||
--> $DIR/double-unsafe-attributes.rs:3:3
|
--> $DIR/double-unsafe-attributes.rs:3:3
|
||||||
|
|
|
|
||||||
LL | #[unsafe(unsafe(no_mangle))]
|
LL | #[unsafe(unsafe(no_mangle))]
|
||||||
| ^^^^^^
|
| ^^^^^^ this is not an unsafe attribute
|
||||||
|
|
|
|
||||||
= note: extraneous unsafe is not allowed in attributes
|
= note: extraneous unsafe is not allowed in attributes
|
||||||
|
|
||||||
|
|
31
tests/ui/attributes/unsafe/extraneous-unsafe-attributes.rs
Normal file
31
tests/ui/attributes/unsafe/extraneous-unsafe-attributes.rs
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
//@ edition: 2024
|
||||||
|
//@ compile-flags: -Zunstable-options
|
||||||
|
#![feature(unsafe_attributes)]
|
||||||
|
|
||||||
|
#[unsafe(cfg(any()))] //~ ERROR: is not an unsafe attribute
|
||||||
|
fn a() {}
|
||||||
|
|
||||||
|
#[unsafe(cfg_attr(any(), allow(dead_code)))] //~ ERROR: is not an unsafe attribute
|
||||||
|
fn b() {}
|
||||||
|
|
||||||
|
#[unsafe(test)] //~ ERROR: is not an unsafe attribute
|
||||||
|
fn aa() {}
|
||||||
|
|
||||||
|
#[unsafe(ignore = "test")] //~ ERROR: is not an unsafe attribute
|
||||||
|
fn bb() {}
|
||||||
|
|
||||||
|
#[unsafe(should_panic(expected = "test"))] //~ ERROR: is not an unsafe attribute
|
||||||
|
fn cc() {}
|
||||||
|
|
||||||
|
#[unsafe(macro_use)] //~ ERROR: is not an unsafe attribute
|
||||||
|
mod inner {
|
||||||
|
#[unsafe(macro_export)] //~ ERROR: is not an unsafe attribute
|
||||||
|
macro_rules! m {
|
||||||
|
() => {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(used)] //~ ERROR: is not an unsafe attribute
|
||||||
|
static FOO: usize = 0;
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,66 @@
|
||||||
|
error: `cfg` is not an unsafe attribute
|
||||||
|
--> $DIR/extraneous-unsafe-attributes.rs:5:3
|
||||||
|
|
|
||||||
|
LL | #[unsafe(cfg(any()))]
|
||||||
|
| ^^^^^^ this is not an unsafe attribute
|
||||||
|
|
|
||||||
|
= note: extraneous unsafe is not allowed in attributes
|
||||||
|
|
||||||
|
error: `cfg_attr` is not an unsafe attribute
|
||||||
|
--> $DIR/extraneous-unsafe-attributes.rs:8:3
|
||||||
|
|
|
||||||
|
LL | #[unsafe(cfg_attr(any(), allow(dead_code)))]
|
||||||
|
| ^^^^^^ this is not an unsafe attribute
|
||||||
|
|
|
||||||
|
= note: extraneous unsafe is not allowed in attributes
|
||||||
|
|
||||||
|
error: `test` is not an unsafe attribute
|
||||||
|
--> $DIR/extraneous-unsafe-attributes.rs:11:3
|
||||||
|
|
|
||||||
|
LL | #[unsafe(test)]
|
||||||
|
| ^^^^^^ this is not an unsafe attribute
|
||||||
|
|
|
||||||
|
= note: extraneous unsafe is not allowed in attributes
|
||||||
|
|
||||||
|
error: `ignore` is not an unsafe attribute
|
||||||
|
--> $DIR/extraneous-unsafe-attributes.rs:14:3
|
||||||
|
|
|
||||||
|
LL | #[unsafe(ignore = "test")]
|
||||||
|
| ^^^^^^ this is not an unsafe attribute
|
||||||
|
|
|
||||||
|
= note: extraneous unsafe is not allowed in attributes
|
||||||
|
|
||||||
|
error: `should_panic` is not an unsafe attribute
|
||||||
|
--> $DIR/extraneous-unsafe-attributes.rs:17:3
|
||||||
|
|
|
||||||
|
LL | #[unsafe(should_panic(expected = "test"))]
|
||||||
|
| ^^^^^^ this is not an unsafe attribute
|
||||||
|
|
|
||||||
|
= note: extraneous unsafe is not allowed in attributes
|
||||||
|
|
||||||
|
error: `macro_use` is not an unsafe attribute
|
||||||
|
--> $DIR/extraneous-unsafe-attributes.rs:20:3
|
||||||
|
|
|
||||||
|
LL | #[unsafe(macro_use)]
|
||||||
|
| ^^^^^^ this is not an unsafe attribute
|
||||||
|
|
|
||||||
|
= note: extraneous unsafe is not allowed in attributes
|
||||||
|
|
||||||
|
error: `macro_export` is not an unsafe attribute
|
||||||
|
--> $DIR/extraneous-unsafe-attributes.rs:22:7
|
||||||
|
|
|
||||||
|
LL | #[unsafe(macro_export)]
|
||||||
|
| ^^^^^^ this is not an unsafe attribute
|
||||||
|
|
|
||||||
|
= note: extraneous unsafe is not allowed in attributes
|
||||||
|
|
||||||
|
error: `used` is not an unsafe attribute
|
||||||
|
--> $DIR/extraneous-unsafe-attributes.rs:28:3
|
||||||
|
|
|
||||||
|
LL | #[unsafe(used)]
|
||||||
|
| ^^^^^^ this is not an unsafe attribute
|
||||||
|
|
|
||||||
|
= note: extraneous unsafe is not allowed in attributes
|
||||||
|
|
||||||
|
error: aborting due to 8 previous errors
|
||||||
|
|
37
tests/ui/attributes/unsafe/proc-unsafe-attributes.rs
Normal file
37
tests/ui/attributes/unsafe/proc-unsafe-attributes.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#![feature(unsafe_attributes)]
|
||||||
|
|
||||||
|
#[unsafe(proc_macro)]
|
||||||
|
//~^ ERROR: is not an unsafe attribute
|
||||||
|
//~| ERROR attribute is only usable with crates of the `proc-macro` crate type
|
||||||
|
pub fn a() {}
|
||||||
|
|
||||||
|
|
||||||
|
#[unsafe(proc_macro_derive(Foo))]
|
||||||
|
//~^ ERROR: is not an unsafe attribute
|
||||||
|
//~| ERROR attribute is only usable with crates of the `proc-macro` crate type
|
||||||
|
pub fn b() {}
|
||||||
|
|
||||||
|
#[proc_macro_derive(unsafe(Foo))]
|
||||||
|
//~^ ERROR attribute is only usable with crates of the `proc-macro` crate type
|
||||||
|
//~| ERROR: expected identifier, found keyword `unsafe`
|
||||||
|
pub fn c() {}
|
||||||
|
|
||||||
|
#[unsafe(proc_macro_attribute)]
|
||||||
|
//~^ ERROR: is not an unsafe attribute
|
||||||
|
//~| ERROR attribute is only usable with crates of the `proc-macro` crate type
|
||||||
|
pub fn d() {}
|
||||||
|
|
||||||
|
#[unsafe(allow(dead_code))]
|
||||||
|
//~^ ERROR: is not an unsafe attribute
|
||||||
|
pub fn e() {}
|
||||||
|
|
||||||
|
#[unsafe(allow(unsafe(dead_code)))]
|
||||||
|
//~^ ERROR: is not an unsafe attribute
|
||||||
|
//~| ERROR: malformed lint attribute input
|
||||||
|
//~| ERROR: malformed lint attribute input
|
||||||
|
//~| ERROR: expected identifier, found keyword `unsafe`
|
||||||
|
//~| ERROR: malformed lint attribute input
|
||||||
|
//~| ERROR: malformed lint attribute input
|
||||||
|
pub fn f() {}
|
||||||
|
|
||||||
|
fn main() {}
|
119
tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr
Normal file
119
tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
error[E0452]: malformed lint attribute input
|
||||||
|
--> $DIR/proc-unsafe-attributes.rs:28:16
|
||||||
|
|
|
||||||
|
LL | #[unsafe(allow(unsafe(dead_code)))]
|
||||||
|
| ^^^^^^^^^^^^^^^^^ bad attribute argument
|
||||||
|
|
||||||
|
error[E0452]: malformed lint attribute input
|
||||||
|
--> $DIR/proc-unsafe-attributes.rs:28:16
|
||||||
|
|
|
||||||
|
LL | #[unsafe(allow(unsafe(dead_code)))]
|
||||||
|
| ^^^^^^^^^^^^^^^^^ bad attribute argument
|
||||||
|
|
|
||||||
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
|
error: `proc_macro` is not an unsafe attribute
|
||||||
|
--> $DIR/proc-unsafe-attributes.rs:3:3
|
||||||
|
|
|
||||||
|
LL | #[unsafe(proc_macro)]
|
||||||
|
| ^^^^^^ this is not an unsafe attribute
|
||||||
|
|
|
||||||
|
= note: extraneous unsafe is not allowed in attributes
|
||||||
|
|
||||||
|
error: `proc_macro_derive` is not an unsafe attribute
|
||||||
|
--> $DIR/proc-unsafe-attributes.rs:9:3
|
||||||
|
|
|
||||||
|
LL | #[unsafe(proc_macro_derive(Foo))]
|
||||||
|
| ^^^^^^ this is not an unsafe attribute
|
||||||
|
|
|
||||||
|
= note: extraneous unsafe is not allowed in attributes
|
||||||
|
|
||||||
|
error: expected identifier, found keyword `unsafe`
|
||||||
|
--> $DIR/proc-unsafe-attributes.rs:14:21
|
||||||
|
|
|
||||||
|
LL | #[proc_macro_derive(unsafe(Foo))]
|
||||||
|
| ^^^^^^ expected identifier, found keyword
|
||||||
|
|
|
||||||
|
help: escape `unsafe` to use it as an identifier
|
||||||
|
|
|
||||||
|
LL | #[proc_macro_derive(r#unsafe(Foo))]
|
||||||
|
| ++
|
||||||
|
|
||||||
|
error: `proc_macro_attribute` is not an unsafe attribute
|
||||||
|
--> $DIR/proc-unsafe-attributes.rs:19:3
|
||||||
|
|
|
||||||
|
LL | #[unsafe(proc_macro_attribute)]
|
||||||
|
| ^^^^^^ this is not an unsafe attribute
|
||||||
|
|
|
||||||
|
= note: extraneous unsafe is not allowed in attributes
|
||||||
|
|
||||||
|
error: `allow` is not an unsafe attribute
|
||||||
|
--> $DIR/proc-unsafe-attributes.rs:24:3
|
||||||
|
|
|
||||||
|
LL | #[unsafe(allow(dead_code))]
|
||||||
|
| ^^^^^^ this is not an unsafe attribute
|
||||||
|
|
|
||||||
|
= note: extraneous unsafe is not allowed in attributes
|
||||||
|
|
||||||
|
error: `allow` is not an unsafe attribute
|
||||||
|
--> $DIR/proc-unsafe-attributes.rs:28:3
|
||||||
|
|
|
||||||
|
LL | #[unsafe(allow(unsafe(dead_code)))]
|
||||||
|
| ^^^^^^ this is not an unsafe attribute
|
||||||
|
|
|
||||||
|
= note: extraneous unsafe is not allowed in attributes
|
||||||
|
|
||||||
|
error: expected identifier, found keyword `unsafe`
|
||||||
|
--> $DIR/proc-unsafe-attributes.rs:28:16
|
||||||
|
|
|
||||||
|
LL | #[unsafe(allow(unsafe(dead_code)))]
|
||||||
|
| ^^^^^^ expected identifier, found keyword
|
||||||
|
|
|
||||||
|
help: escape `unsafe` to use it as an identifier
|
||||||
|
|
|
||||||
|
LL | #[unsafe(allow(r#unsafe(dead_code)))]
|
||||||
|
| ++
|
||||||
|
|
||||||
|
error: the `#[proc_macro]` attribute is only usable with crates of the `proc-macro` crate type
|
||||||
|
--> $DIR/proc-unsafe-attributes.rs:3:1
|
||||||
|
|
|
||||||
|
LL | #[unsafe(proc_macro)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: the `#[proc_macro_derive]` attribute is only usable with crates of the `proc-macro` crate type
|
||||||
|
--> $DIR/proc-unsafe-attributes.rs:9:1
|
||||||
|
|
|
||||||
|
LL | #[unsafe(proc_macro_derive(Foo))]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: the `#[proc_macro_derive]` attribute is only usable with crates of the `proc-macro` crate type
|
||||||
|
--> $DIR/proc-unsafe-attributes.rs:14:1
|
||||||
|
|
|
||||||
|
LL | #[proc_macro_derive(unsafe(Foo))]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: the `#[proc_macro_attribute]` attribute is only usable with crates of the `proc-macro` crate type
|
||||||
|
--> $DIR/proc-unsafe-attributes.rs:19:1
|
||||||
|
|
|
||||||
|
LL | #[unsafe(proc_macro_attribute)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error[E0452]: malformed lint attribute input
|
||||||
|
--> $DIR/proc-unsafe-attributes.rs:28:16
|
||||||
|
|
|
||||||
|
LL | #[unsafe(allow(unsafe(dead_code)))]
|
||||||
|
| ^^^^^^^^^^^^^^^^^ bad attribute argument
|
||||||
|
|
|
||||||
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
|
error[E0452]: malformed lint attribute input
|
||||||
|
--> $DIR/proc-unsafe-attributes.rs:28:16
|
||||||
|
|
|
||||||
|
LL | #[unsafe(allow(unsafe(dead_code)))]
|
||||||
|
| ^^^^^^^^^^^^^^^^^ bad attribute argument
|
||||||
|
|
|
||||||
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
|
error: aborting due to 15 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0452`.
|
|
@ -4,4 +4,10 @@
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
fn a() {}
|
fn a() {}
|
||||||
|
|
||||||
|
#[unsafe(export_name = "foo")]
|
||||||
|
fn b() {}
|
||||||
|
|
||||||
|
#[cfg_attr(any(), unsafe(no_mangle))]
|
||||||
|
static VAR2: u32 = 1;
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -2,7 +2,7 @@ error: `repr` is not an unsafe attribute
|
||||||
--> $DIR/unsafe-safe-attribute.rs:3:3
|
--> $DIR/unsafe-safe-attribute.rs:3:3
|
||||||
|
|
|
|
||||||
LL | #[unsafe(repr(C))]
|
LL | #[unsafe(repr(C))]
|
||||||
| ^^^^^^
|
| ^^^^^^ this is not an unsafe attribute
|
||||||
|
|
|
|
||||||
= note: extraneous unsafe is not allowed in attributes
|
= note: extraneous unsafe is not allowed in attributes
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ error: `diagnostic::on_unimplemented` is not an unsafe attribute
|
||||||
--> $DIR/unsafe-safe-attribute_diagnostic.rs:3:3
|
--> $DIR/unsafe-safe-attribute_diagnostic.rs:3:3
|
||||||
|
|
|
|
||||||
LL | #[unsafe(diagnostic::on_unimplemented(
|
LL | #[unsafe(diagnostic::on_unimplemented(
|
||||||
| ^^^^^^
|
| ^^^^^^ this is not an unsafe attribute
|
||||||
|
|
|
|
||||||
= note: extraneous unsafe is not allowed in attributes
|
= note: extraneous unsafe is not allowed in attributes
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue