about summary refs log tree commit diff
diff options
context:
space:
mode:
authorcarbotaniuman <41451839+carbotaniuman@users.noreply.github.com>2024-07-09 19:06:49 -0500
committercarbotaniuman <41451839+carbotaniuman@users.noreply.github.com>2024-07-30 18:28:43 -0500
commit49db8a5a999f0908b5bf74b88a2d72a4f4dddf48 (patch)
treea080b5f39b3e923e2a2057102cf88362757f316c
parentd8bc8761a5bb8456c0b7940434b3b3b4f0ed035c (diff)
downloadrust-49db8a5a999f0908b5bf74b88a2d72a4f4dddf48.tar.gz
rust-49db8a5a999f0908b5bf74b88a2d72a4f4dddf48.zip
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(...)`.
-rw-r--r--compiler/rustc_builtin_macros/messages.ftl3
-rw-r--r--compiler/rustc_builtin_macros/src/cfg.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/derive.rs13
-rw-r--r--compiler/rustc_builtin_macros/src/errors.rs7
-rw-r--r--compiler/rustc_expand/src/config.rs4
-rw-r--r--compiler/rustc_interface/src/interface.rs5
-rw-r--r--compiler/rustc_parse/messages.ftl1
-rw-r--r--compiler/rustc_parse/src/errors.rs1
-rw-r--r--compiler/rustc_parse/src/parser/attr.rs21
-rw-r--r--compiler/rustc_parse/src/validate_attr.rs82
-rw-r--r--tests/ui/attributes/unsafe/derive-unsafe-attributes.rs8
-rw-r--r--tests/ui/attributes/unsafe/derive-unsafe-attributes.stderr59
-rw-r--r--tests/ui/attributes/unsafe/double-unsafe-attributes.stderr2
-rw-r--r--tests/ui/attributes/unsafe/extraneous-unsafe-attributes.stderr16
-rw-r--r--tests/ui/attributes/unsafe/proc-unsafe-attributes.rs10
-rw-r--r--tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr77
-rw-r--r--tests/ui/attributes/unsafe/unsafe-attributes.rs6
-rw-r--r--tests/ui/attributes/unsafe/unsafe-safe-attribute.stderr2
-rw-r--r--tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr2
19 files changed, 226 insertions, 96 deletions
diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl
index a30ab236213..ae3cce40f5d 100644
--- a/compiler/rustc_builtin_macros/messages.ftl
+++ b/compiler/rustc_builtin_macros/messages.ftl
@@ -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
     .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
diff --git a/compiler/rustc_builtin_macros/src/cfg.rs b/compiler/rustc_builtin_macros/src/cfg.rs
index ceb62230ece..de198115fa0 100644
--- a/compiler/rustc_builtin_macros/src/cfg.rs
+++ b/compiler/rustc_builtin_macros/src/cfg.rs
@@ -6,6 +6,7 @@ use rustc_ast::token;
 use rustc_ast::tokenstream::TokenStream;
 use rustc_errors::PResult;
 use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult};
+use rustc_parse::parser::attr::AllowLeadingUnsafe;
 use rustc_span::Span;
 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 }));
     }
 
-    let cfg = p.parse_meta_item()?;
+    let cfg = p.parse_meta_item(AllowLeadingUnsafe::Yes)?;
 
     let _ = p.eat(&token::Comma);
 
diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs
index c540ff4e1de..57bddf0ab60 100644
--- a/compiler/rustc_builtin_macros/src/derive.rs
+++ b/compiler/rustc_builtin_macros/src/derive.rs
@@ -1,5 +1,5 @@
 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::{
     Annotatable, DeriveResolution, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier,
 };
@@ -62,7 +62,6 @@ impl MultiItemModifier for Expander {
                                 // 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 {
@@ -162,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!(),
-    }
-}
diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs
index 0a4f07709c7..93fc9bcb9d2 100644
--- a/compiler/rustc_builtin_macros/src/errors.rs
+++ b/compiler/rustc_builtin_macros/src/errors.rs
@@ -298,13 +298,6 @@ pub(crate) struct DerivePathArgsValue {
 }
 
 #[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]
 pub(crate) struct NoDefaultVariant {
diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs
index d46ec7a9ac0..f6bf9f5e89f 100644
--- a/compiler/rustc_expand/src/config.rs
+++ b/compiler/rustc_expand/src/config.rs
@@ -8,7 +8,9 @@ use rustc_ast::tokenstream::{
 use rustc_ast::{self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, NodeId};
 use rustc_attr as attr;
 use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
-use rustc_feature::{AttributeSafety, 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_parse::validate_attr;
 use rustc_session::parse::feature_err;
diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs
index 886b043af24..04e2b7d45dc 100644
--- a/compiler/rustc_interface/src/interface.rs
+++ b/compiler/rustc_interface/src/interface.rs
@@ -15,6 +15,7 @@ use rustc_middle::ty;
 use rustc_middle::ty::CurrentGcx;
 use rustc_middle::util::Providers;
 use rustc_parse::new_parser_from_source_str;
+use rustc_parse::parser::attr::AllowLeadingUnsafe;
 use rustc_query_impl::QueryCtxt;
 use rustc_query_system::query::print_query_stack;
 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()) {
-                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 => {
                         if meta_item.path.segments.len() != 1 {
                             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(..) => expected_error(),
             Err(err) => {
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index 391a5791776..e13306b9d9f 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -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
 
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 10762803708..e5b0dc804a5 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -3188,6 +3188,7 @@ pub(crate) struct DotDotRangeAttribute {
 #[note]
 pub struct InvalidAttrUnsafe {
     #[primary_span]
+    #[label]
     pub span: Span,
     pub name: Path,
 }
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index 12b9414d1f7..f34ef071e21 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -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
         }
diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs
index af2db171840..a64c00f3b6c 100644
--- a/compiler/rustc_parse/src/validate_attr.rs
+++ b/compiler/rustc_parse/src/validate_attr.rs
@@ -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(),
+            });
+        }
     }
 }
 
diff --git a/tests/ui/attributes/unsafe/derive-unsafe-attributes.rs b/tests/ui/attributes/unsafe/derive-unsafe-attributes.rs
index 7f0d6a0cf8b..b8edb4aab90 100644
--- a/tests/ui/attributes/unsafe/derive-unsafe-attributes.rs
+++ b/tests/ui/attributes/unsafe/derive-unsafe-attributes.rs
@@ -1,6 +1,12 @@
 #![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;
 
 #[unsafe(derive(Debug))] //~ ERROR: is not an unsafe attribute
diff --git a/tests/ui/attributes/unsafe/derive-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/derive-unsafe-attributes.stderr
index 22010c61b5f..c40a5512fd5 100644
--- a/tests/ui/attributes/unsafe/derive-unsafe-attributes.stderr
+++ b/tests/ui/attributes/unsafe/derive-unsafe-attributes.stderr
@@ -1,16 +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:6:3
+  --> $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: aborting due to 2 previous errors
+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
+   |
+LL | #[derive(unsafe(Debug))]
+   |          ^^^^^^
+
+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
 
diff --git a/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr
index ea82bac6df0..950b2636993 100644
--- a/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr
+++ b/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr
@@ -13,7 +13,7 @@ error: `r#unsafe` is not an unsafe attribute
   --> $DIR/double-unsafe-attributes.rs:3:3
    |
 LL | #[unsafe(unsafe(no_mangle))]
-   |   ^^^^^^
+   |   ^^^^^^ this is not an unsafe attribute
    |
    = note: extraneous unsafe is not allowed in attributes
 
diff --git a/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.stderr
index a2e56087d16..f39074b613d 100644
--- a/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.stderr
+++ b/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.stderr
@@ -2,7 +2,7 @@ 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
 
@@ -10,7 +10,7 @@ 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
 
@@ -18,7 +18,7 @@ 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
 
@@ -26,7 +26,7 @@ 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
 
@@ -34,7 +34,7 @@ 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
 
@@ -42,7 +42,7 @@ 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
 
@@ -50,7 +50,7 @@ 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
 
@@ -58,7 +58,7 @@ 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
 
diff --git a/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs b/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs
index caf6c6dc8ff..f29a5b3252b 100644
--- a/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs
+++ b/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs
@@ -13,6 +13,7 @@ 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)]
@@ -24,4 +25,13 @@ pub fn d() {}
 //~^ 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() {}
diff --git a/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr
index 346d2c2e9ae..79d34d458bd 100644
--- a/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr
+++ b/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr
@@ -1,8 +1,22 @@
+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
 
@@ -10,26 +24,56 @@ 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:18:3
+  --> $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:23:3
+  --> $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
    |
@@ -49,10 +93,27 @@ 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:18:1
+  --> $DIR/proc-unsafe-attributes.rs:19:1
    |
 LL | #[unsafe(proc_macro_attribute)]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 8 previous errors
+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`.
diff --git a/tests/ui/attributes/unsafe/unsafe-attributes.rs b/tests/ui/attributes/unsafe/unsafe-attributes.rs
index e7620a18048..33a412add50 100644
--- a/tests/ui/attributes/unsafe/unsafe-attributes.rs
+++ b/tests/ui/attributes/unsafe/unsafe-attributes.rs
@@ -4,4 +4,10 @@
 #[unsafe(no_mangle)]
 fn a() {}
 
+#[unsafe(export_name = "foo")]
+fn b() {}
+
+#[cfg_attr(any(), unsafe(no_mangle))]
+static VAR2: u32 = 1;
+
 fn main() {}
diff --git a/tests/ui/attributes/unsafe/unsafe-safe-attribute.stderr b/tests/ui/attributes/unsafe/unsafe-safe-attribute.stderr
index 0602af34e4f..584b0ea797d 100644
--- a/tests/ui/attributes/unsafe/unsafe-safe-attribute.stderr
+++ b/tests/ui/attributes/unsafe/unsafe-safe-attribute.stderr
@@ -2,7 +2,7 @@ error: `repr` is not an unsafe attribute
   --> $DIR/unsafe-safe-attribute.rs:3:3
    |
 LL | #[unsafe(repr(C))]
-   |   ^^^^^^
+   |   ^^^^^^ this is not an unsafe attribute
    |
    = note: extraneous unsafe is not allowed in attributes
 
diff --git a/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr b/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr
index 584dacf4d8c..26b5e4e37b9 100644
--- a/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr
+++ b/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr
@@ -2,7 +2,7 @@ error: `diagnostic::on_unimplemented` is not an unsafe attribute
   --> $DIR/unsafe-safe-attribute_diagnostic.rs:3:3
    |
 LL | #[unsafe(diagnostic::on_unimplemented(
-   |   ^^^^^^
+   |   ^^^^^^ this is not an unsafe attribute
    |
    = note: extraneous unsafe is not allowed in attributes