diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 79717c969d79a..941bb78c0dd63 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -899,7 +899,7 @@ fn validate_generic_param_order(dcx: DiagCtxtHandle<'_>, generics: &[GenericPara impl<'a> Visitor<'a> for AstValidator<'a> { fn visit_attribute(&mut self, attr: &Attribute) { - validate_attr::check_attr(&self.session.psess, attr); + validate_attr::check_attr(&self.features, &self.session.psess, attr); } fn visit_ty(&mut self, ty: &'a Ty) { diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 716bfc8c26b1e..5a807d1dba8e9 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1882,7 +1882,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { let mut span: Option = None; while let Some(attr) = attrs.next() { rustc_ast_passes::feature_gate::check_attribute(attr, self.cx.sess, features); - validate_attr::check_attr(&self.cx.sess.psess, attr); + validate_attr::check_attr(features, &self.cx.sess.psess, attr); let current_span = if let Some(sp) = span { sp.to(attr.span) } else { attr.span }; span = Some(current_span); diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 9e2756f07eded..5e83e0d27e198 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1145,10 +1145,6 @@ 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> = LazyLock::new(|| { let mut map = FxHashMap::default(); diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index fb3b7c0a12729..bf42936431837 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -125,7 +125,7 @@ pub use accepted::ACCEPTED_FEATURES; pub use builtin_attrs::AttributeDuplicates; pub use builtin_attrs::{ deprecated_attributes, encode_cross_crate, find_gated_cfg, is_builtin_attr_name, - is_unsafe_attr, is_valid_for_get_attr, AttributeGate, AttributeTemplate, AttributeType, + is_valid_for_get_attr, AttributeGate, AttributeSafety, AttributeTemplate, AttributeType, BuiltinAttribute, GatedCfg, BUILTIN_ATTRIBUTES, BUILTIN_ATTRIBUTE_MAP, }; pub use removed::REMOVED_FEATURES; diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index fdedf2c2e6d38..eac5083ffbf0c 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -825,6 +825,10 @@ lint_unnameable_test_items = cannot test inner items lint_unnecessary_qualification = unnecessary qualification .suggestion = remove the unnecessary path segments +lint_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe + .label = usage of unsafe attribute +lint_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)` + lint_unsupported_group = `{$lint_group}` lint group is not supported with ´--force-warn´ lint_untranslatable_diag = diagnostics should be created using translatable messages diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs index 290bb5173dbe7..adb2a3275c0aa 100644 --- a/compiler/rustc_lint/src/context/diagnostics.rs +++ b/compiler/rustc_lint/src/context/diagnostics.rs @@ -319,6 +319,16 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: & BuiltinLintDiag::UnusedQualifications { removal_span } => { lints::UnusedQualifications { removal_span }.decorate_lint(diag); } + BuiltinLintDiag::UnsafeAttrOutsideUnsafe { + attribute_name_span, + sugg_spans: (left, right), + } => { + lints::UnsafeAttrOutsideUnsafe { + span: attribute_name_span, + suggestion: lints::UnsafeAttrOutsideUnsafeSuggestion { left, right }, + } + .decorate_lint(diag); + } BuiltinLintDiag::AssociatedConstElidedLifetime { elided, span: lt_span, diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index b26d04d061805..6df3a11deb00b 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2890,3 +2890,24 @@ pub struct RedundantImportVisibility { pub import_vis: String, pub max_vis: String, } + +#[derive(LintDiagnostic)] +#[diag(lint_unsafe_attr_outside_unsafe)] +pub struct UnsafeAttrOutsideUnsafe { + #[label] + pub span: Span, + #[subdiagnostic] + pub suggestion: UnsafeAttrOutsideUnsafeSuggestion, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + lint_unsafe_attr_outside_unsafe_suggestion, + applicability = "machine-applicable" +)] +pub struct UnsafeAttrOutsideUnsafeSuggestion { + #[suggestion_part(code = "unsafe(")] + pub left: Span, + #[suggestion_part(code = ")")] + pub right: Span, +} diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 1913b9d6a1c31..265779c937474 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -115,6 +115,7 @@ declare_lint_pass! { UNNAMEABLE_TYPES, UNREACHABLE_CODE, UNREACHABLE_PATTERNS, + UNSAFE_ATTR_OUTSIDE_UNSAFE, UNSAFE_OP_IN_UNSAFE_FN, UNSTABLE_NAME_COLLISIONS, UNSTABLE_SYNTAX_PRE_EXPANSION, @@ -4902,3 +4903,45 @@ declare_lint! { reference: "issue #123743 ", }; } + +declare_lint! { + /// The `unsafe_attr_outside_unsafe` lint detects a missing unsafe keyword + /// on attributes considered unsafe. + /// + /// ### Example + /// + /// ```rust + /// #![feature(unsafe_attributes)] + /// #![warn(unsafe_attr_outside_unsafe)] + /// + /// #[no_mangle] + /// extern "C" fn foo() {} + /// + /// fn main() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Some attributes (e.g. `no_mangle`, `export_name`, `link_section` -- see + /// [issue #82499] for a more complete list) are considered "unsafe" attributes. + /// An unsafe attribute must only be used inside unsafe(...). + /// + /// This lint can automatically wrap the attributes in `unsafe(...)` , but this + /// obviously cannot verify that the preconditions of the `unsafe` + /// attributes are fulfilled, so that is still up to the user. + /// + /// The lint is currently "allow" by default, but that might change in the + /// future. + /// + /// [editions]: https://doc.rust-lang.org/edition-guide/ + /// [issue #82499]: https://github.com/rust-lang/rust/issues/82499 + pub UNSAFE_ATTR_OUTSIDE_UNSAFE, + Allow, + "detects unsafe attributes outside of unsafe", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024), + reference: "issue #123757 ", + }; +} diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index b3838f915f6b2..f33aadfbbc84d 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -691,6 +691,10 @@ pub enum BuiltinLintDiag { /// The span of the unnecessarily-qualified path to remove. removal_span: Span, }, + UnsafeAttrOutsideUnsafe { + attribute_name_span: Span, + sugg_spans: (Span, Span), + }, AssociatedConstElidedLifetime { elided: bool, span: Span, diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index efb9526eabcfd..f08efe60d96b8 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -366,6 +366,10 @@ parse_inner_doc_comment_not_permitted = expected outer doc comment .label_does_not_annotate_this = the inner doc comment doesn't annotate this {$item} .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 + .suggestion = remove the `unsafe(...)` + .note = extraneous unsafe is not allowed in attributes + parse_invalid_block_macro_segment = cannot use a `block` macro fragment here .label = the `block` fragment is within this context .suggestion = wrap this in another block @@ -866,6 +870,11 @@ parse_unmatched_angle_brackets = {$num_extra_brackets -> *[other] remove extra angle brackets } +parse_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe + .label = usage of unsafe attribute +parse_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)` + + parse_unskipped_whitespace = whitespace symbol '{$ch}' is not skipped .label = {parse_unskipped_whitespace} diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 46e1573485304..8d49887f16441 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2997,3 +2997,34 @@ pub(crate) struct DotDotRangeAttribute { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(parse_invalid_attr_unsafe)] +#[note] +pub struct InvalidAttrUnsafe { + #[primary_span] + pub span: Span, + pub name: Path, +} + +#[derive(Diagnostic)] +#[diag(parse_unsafe_attr_outside_unsafe)] +pub struct UnsafeAttrOutsideUnsafe { + #[primary_span] + #[label] + pub span: Span, + #[subdiagnostic] + pub suggestion: UnsafeAttrOutsideUnsafeSuggestion, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + parse_unsafe_attr_outside_unsafe_suggestion, + applicability = "machine-applicable" +)] +pub struct UnsafeAttrOutsideUnsafeSuggestion { + #[suggestion_part(code = "unsafe(")] + pub left: Span, + #[suggestion_part(code = ")")] + pub right: Span, +} diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 4ca52146039c6..bcb1131cc1961 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -5,21 +5,73 @@ use crate::{errors, parse_in}; use rustc_ast::token::Delimiter; use rustc_ast::tokenstream::DelimSpan; use rustc_ast::MetaItemKind; -use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, Attribute, DelimArgs, MetaItem}; +use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, Attribute, DelimArgs, MetaItem, Safety}; use rustc_errors::{Applicability, FatalError, PResult}; -use rustc_feature::{AttributeTemplate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; +use rustc_feature::{ + AttributeSafety, AttributeTemplate, BuiltinAttribute, Features, BUILTIN_ATTRIBUTE_MAP, +}; use rustc_session::errors::report_lit_error; -use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT; +use rustc_session::lint::builtin::{ILL_FORMED_ATTRIBUTE_INPUT, UNSAFE_ATTR_OUTSIDE_UNSAFE}; use rustc_session::lint::BuiltinLintDiag; use rustc_session::parse::ParseSess; -use rustc_span::{sym, Span, Symbol}; +use rustc_span::{sym, BytePos, Span, Symbol}; -pub fn check_attr(psess: &ParseSess, attr: &Attribute) { +pub fn check_attr(features: &Features, psess: &ParseSess, attr: &Attribute) { if attr.is_doc_comment() { return; } let attr_info = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)); + let attr_item = attr.get_normal_item(); + + let is_unsafe_attr = attr_info.is_some_and(|attr| attr.safety == AttributeSafety::Unsafe); + + if features.unsafe_attributes { + 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. match attr_info { @@ -32,7 +84,7 @@ pub fn check_attr(psess: &ParseSess, attr: &Attribute) { } } } - _ if let AttrArgs::Eq(..) = attr.get_normal_item().args => { + _ if let AttrArgs::Eq(..) = attr_item.args => { // All key-value attributes are restricted to meta-item syntax. match parse_meta(psess, attr) { Ok(_) => {} diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 84c71c4bed29b..9a830b0f49bac 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -384,10 +384,6 @@ 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 diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index a0b3470df6dba..2ed5bba85c60b 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -10,9 +10,7 @@ use rustc_ast::{MetaItemKind, MetaItemLit, NestedMetaItem}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, IntoDiagArg, MultiSpan}; use rustc_errors::{DiagCtxtHandle, StashKey}; -use rustc_feature::{ - is_unsafe_attr, AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP, -}; +use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; use rustc_hir::def_id::LocalModDefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{self as hir}; @@ -116,8 +114,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { 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) @@ -312,21 +308,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { 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, diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index d27b94ebd22af..f05965680920a 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -4,7 +4,7 @@ use std::{ }; use crate::fluent_generated as fluent; -use rustc_ast::{ast, Label}; +use rustc_ast::Label; use rustc_errors::{ codes::*, Applicability, Diag, DiagCtxtHandle, DiagSymbolList, Diagnostic, EmissionGuarantee, Level, MultiSpan, SubdiagMessageOp, Subdiagnostic, @@ -863,15 +863,6 @@ pub struct InvalidAttrAtCrateLevel { pub item: Option, } -#[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, diff --git a/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr index 1c07a5bf8baa0..ea82bac6df07b 100644 --- a/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr +++ b/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr @@ -9,12 +9,6 @@ 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 | @@ -23,5 +17,11 @@ LL | #[unsafe(unsafe(no_mangle))] | = note: extraneous unsafe is not allowed in attributes +error: cannot find attribute `r#unsafe` in this scope + --> $DIR/double-unsafe-attributes.rs:3:10 + | +LL | #[unsafe(unsafe(no_mangle))] + | ^^^^^^ + error: aborting due to 3 previous errors diff --git a/tests/ui/rust-2024/unsafe-attributes/in_2024_compatibility.rs b/tests/ui/rust-2024/unsafe-attributes/in_2024_compatibility.rs new file mode 100644 index 0000000000000..c6f9115cde750 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-attributes/in_2024_compatibility.rs @@ -0,0 +1,9 @@ +#![deny(rust_2024_compatibility)] +#![feature(unsafe_attributes)] + +#[no_mangle] +//~^ ERROR: unsafe attribute used without unsafe +//~| WARN this is accepted in the current edition +extern "C" fn foo() {} + +fn main() {} diff --git a/tests/ui/rust-2024/unsafe-attributes/in_2024_compatibility.stderr b/tests/ui/rust-2024/unsafe-attributes/in_2024_compatibility.stderr new file mode 100644 index 0000000000000..f0689d9883c9e --- /dev/null +++ b/tests/ui/rust-2024/unsafe-attributes/in_2024_compatibility.stderr @@ -0,0 +1,21 @@ +error: unsafe attribute used without unsafe + --> $DIR/in_2024_compatibility.rs:4:3 + | +LL | #[no_mangle] + | ^^^^^^^^^ usage of unsafe attribute + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024! + = note: for more information, see issue #123757 +note: the lint level is defined here + --> $DIR/in_2024_compatibility.rs:1:9 + | +LL | #![deny(rust_2024_compatibility)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + = note: `#[deny(unsafe_attr_outside_unsafe)]` implied by `#[deny(rust_2024_compatibility)]` +help: wrap the attribute in `unsafe(...)` + | +LL | #[unsafe(no_mangle)] + | +++++++ + + +error: aborting due to 1 previous error + diff --git a/tests/ui/rust-2024/unsafe-attributes/unsafe-attribute-marked.rs b/tests/ui/rust-2024/unsafe-attributes/unsafe-attribute-marked.rs new file mode 100644 index 0000000000000..279ced2525a23 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-attributes/unsafe-attribute-marked.rs @@ -0,0 +1,12 @@ +//@ revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 +//@[edition2024] compile-flags: -Zunstable-options +//@ check-pass + +#![feature(unsafe_attributes)] + +#[unsafe(no_mangle)] +extern "C" fn foo() {} + +fn main() {} diff --git a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.fixed b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.fixed new file mode 100644 index 0000000000000..6ebdff0334c8b --- /dev/null +++ b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.fixed @@ -0,0 +1,61 @@ +//@ run-rustfix +#![feature(unsafe_attributes)] +#![deny(unsafe_attr_outside_unsafe)] + +macro_rules! tt { + ($e:tt) => { + #$e + extern fn foo() {} + } +} + +macro_rules! ident { + ($e:ident) => { + #[unsafe($e)] + //~^ ERROR: unsafe attribute used without unsafe + //~| WARN this is accepted in the current edition + extern fn bar() {} + } +} + +macro_rules! ident2 { + ($e:ident, $l:literal) => { + #[unsafe($e = $l)] + //~^ ERROR: unsafe attribute used without unsafe + //~| WARN this is accepted in the current edition + extern fn bars() {} + } +} + +macro_rules! meta { + ($m:meta) => { + #[$m] + extern fn baz() {} + } +} + +macro_rules! meta2 { + ($m:meta) => { + #[$m] + extern fn baw() {} + } +} + +tt!([unsafe(no_mangle)]); +//~^ ERROR: unsafe attribute used without unsafe +//~| WARN this is accepted in the current edition +ident!(no_mangle); +meta!(unsafe(no_mangle)); +//~^ ERROR: unsafe attribute used without unsafe +//~| WARN this is accepted in the current edition +meta2!(unsafe(export_name = "baw")); +//~^ ERROR: unsafe attribute used without unsafe +//~| WARN this is accepted in the current edition +ident2!(export_name, "bars"); + +#[unsafe(no_mangle)] +//~^ ERROR: unsafe attribute used without unsafe +//~| WARN this is accepted in the current edition +extern "C" fn one() {} + +fn main() {} diff --git a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.rs b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.rs new file mode 100644 index 0000000000000..c78ff45ea4cdf --- /dev/null +++ b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.rs @@ -0,0 +1,61 @@ +//@ run-rustfix +#![feature(unsafe_attributes)] +#![deny(unsafe_attr_outside_unsafe)] + +macro_rules! tt { + ($e:tt) => { + #$e + extern fn foo() {} + } +} + +macro_rules! ident { + ($e:ident) => { + #[$e] + //~^ ERROR: unsafe attribute used without unsafe + //~| WARN this is accepted in the current edition + extern fn bar() {} + } +} + +macro_rules! ident2 { + ($e:ident, $l:literal) => { + #[$e = $l] + //~^ ERROR: unsafe attribute used without unsafe + //~| WARN this is accepted in the current edition + extern fn bars() {} + } +} + +macro_rules! meta { + ($m:meta) => { + #[$m] + extern fn baz() {} + } +} + +macro_rules! meta2 { + ($m:meta) => { + #[$m] + extern fn baw() {} + } +} + +tt!([no_mangle]); +//~^ ERROR: unsafe attribute used without unsafe +//~| WARN this is accepted in the current edition +ident!(no_mangle); +meta!(no_mangle); +//~^ ERROR: unsafe attribute used without unsafe +//~| WARN this is accepted in the current edition +meta2!(export_name = "baw"); +//~^ ERROR: unsafe attribute used without unsafe +//~| WARN this is accepted in the current edition +ident2!(export_name, "bars"); + +#[no_mangle] +//~^ ERROR: unsafe attribute used without unsafe +//~| WARN this is accepted in the current edition +extern "C" fn one() {} + +fn main() {} diff --git a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.stderr b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.stderr new file mode 100644 index 0000000000000..c95984f58ecf7 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.stderr @@ -0,0 +1,93 @@ +error: unsafe attribute used without unsafe + --> $DIR/unsafe-attributes-fix.rs:44:6 + | +LL | tt!([no_mangle]); + | ^^^^^^^^^ usage of unsafe attribute + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024! + = note: for more information, see issue #123757 +note: the lint level is defined here + --> $DIR/unsafe-attributes-fix.rs:3:9 + | +LL | #![deny(unsafe_attr_outside_unsafe)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: wrap the attribute in `unsafe(...)` + | +LL | tt!([unsafe(no_mangle)]); + | +++++++ + + +error: unsafe attribute used without unsafe + --> $DIR/unsafe-attributes-fix.rs:14:11 + | +LL | #[$e] + | ^^ usage of unsafe attribute +... +LL | ident!(no_mangle); + | ----------------- in this macro invocation + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024! + = note: for more information, see issue #123757 + = note: this error originates in the macro `ident` (in Nightly builds, run with -Z macro-backtrace for more info) +help: wrap the attribute in `unsafe(...)` + | +LL | #[unsafe($e)] + | +++++++ + + +error: unsafe attribute used without unsafe + --> $DIR/unsafe-attributes-fix.rs:48:7 + | +LL | meta!(no_mangle); + | ^^^^^^^^^ usage of unsafe attribute + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024! + = note: for more information, see issue #123757 +help: wrap the attribute in `unsafe(...)` + | +LL | meta!(unsafe(no_mangle)); + | +++++++ + + +error: unsafe attribute used without unsafe + --> $DIR/unsafe-attributes-fix.rs:51:8 + | +LL | meta2!(export_name = "baw"); + | ^^^^^^^^^^^ usage of unsafe attribute + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024! + = note: for more information, see issue #123757 +help: wrap the attribute in `unsafe(...)` + | +LL | meta2!(unsafe(export_name = "baw")); + | +++++++ + + +error: unsafe attribute used without unsafe + --> $DIR/unsafe-attributes-fix.rs:23:11 + | +LL | #[$e = $l] + | ^^ usage of unsafe attribute +... +LL | ident2!(export_name, "bars"); + | ---------------------------- in this macro invocation + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024! + = note: for more information, see issue #123757 + = note: this error originates in the macro `ident2` (in Nightly builds, run with -Z macro-backtrace for more info) +help: wrap the attribute in `unsafe(...)` + | +LL | #[unsafe($e = $l)] + | +++++++ + + +error: unsafe attribute used without unsafe + --> $DIR/unsafe-attributes-fix.rs:56:3 + | +LL | #[no_mangle] + | ^^^^^^^^^ usage of unsafe attribute + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024! + = note: for more information, see issue #123757 +help: wrap the attribute in `unsafe(...)` + | +LL | #[unsafe(no_mangle)] + | +++++++ + + +error: aborting due to 6 previous errors + diff --git a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes.edition2024.stderr b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes.edition2024.stderr new file mode 100644 index 0000000000000..35475d6671626 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes.edition2024.stderr @@ -0,0 +1,13 @@ +error: unsafe attribute used without unsafe + --> $DIR/unsafe-attributes.rs:9:3 + | +LL | #[no_mangle] + | ^^^^^^^^^ usage of unsafe attribute + | +help: wrap the attribute in `unsafe(...)` + | +LL | #[unsafe(no_mangle)] + | +++++++ + + +error: aborting due to 1 previous error + diff --git a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes.rs b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes.rs new file mode 100644 index 0000000000000..3a6af9dfb2b0f --- /dev/null +++ b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes.rs @@ -0,0 +1,12 @@ +//@ revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2021] check-pass +//@[edition2024] edition:2024 +//@[edition2024] compile-flags: -Zunstable-options + +#![feature(unsafe_attributes)] + +#[no_mangle] //[edition2024]~ ERROR: unsafe attribute used without unsafe +extern "C" fn foo() {} + +fn main() {}