diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 8b776edad4d6e..a5f1679119615 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1205,6 +1205,47 @@ impl<'a> AstValidator<'a> { self.visit_vis(vis); self.visit_ident(ident); } + + // Check EII implementation attributes with a allowed list + fn check_eii_impl_attrs(&self, attrs: &[Attribute], eii_impls: &[EiiImpl]) { + if eii_impls.is_empty() { + return; + } + + let allowed_attrs: &[Symbol] = &[ + sym::allow, + sym::warn, + sym::deny, + sym::forbid, + sym::expect, + sym::cfg_trace, + sym::cfg_attr_trace, + sym::doc, + sym::inline, + sym::cold, + sym::optimize, + sym::coverage, + sym::sanitize, + sym::must_use, + sym::deprecated, + ]; + + for attr in attrs { + if attr.is_doc_comment() || attr.has_any_name(allowed_attrs) { + continue; + } + + let attr_name = pprust::path_to_string(&attr.get_normal_item().path); + for eii_impl in eii_impls { + self.dcx().emit_err(diagnostics::EiiImplAttributeNotSupported { + attr_span: attr.span, + attr_name: &attr_name, + eii_span: eii_impl.span, + eii_name: pprust::path_to_string(&eii_impl.eii_macro_path), + }); + } + } + } } /// Checks that generic parameters are in the correct order, @@ -1394,6 +1435,7 @@ impl Visitor<'_> for AstValidator<'_> { for EiiImpl { eii_macro_path, .. } in eii_impls { self.visit_path(eii_macro_path); } + self.check_eii_impl_attrs(&item.attrs, eii_impls); let is_intrinsic = item.attrs.iter().any(|a| a.has_name(sym::rustc_intrinsic)); if body.is_none() && !is_intrinsic && !self.is_sdylib_interface { @@ -1569,8 +1611,9 @@ impl Visitor<'_> for AstValidator<'_> { visit::walk_item(self, item); } - ItemKind::Static(StaticItem { expr, safety, .. }) => { + ItemKind::Static(StaticItem { expr, safety, eii_impls, .. }) => { self.check_item_safety(item.span, *safety); + self.check_eii_impl_attrs(&item.attrs, eii_impls); if matches!(safety, Safety::Unsafe(_)) { self.dcx().emit_err(diagnostics::UnsafeStatic { span: item.span }); } diff --git a/compiler/rustc_ast_passes/src/diagnostics.rs b/compiler/rustc_ast_passes/src/diagnostics.rs index affe69bd7c5e8..2396e2ccfdbaf 100644 --- a/compiler/rustc_ast_passes/src/diagnostics.rs +++ b/compiler/rustc_ast_passes/src/diagnostics.rs @@ -189,6 +189,17 @@ pub(crate) struct FnParamForbiddenAttr { pub span: Span, } +#[derive(Diagnostic)] +#[diag("`#[{$eii_name}]` is not allowed to have `#[{$attr_name}]`")] +pub(crate) struct EiiImplAttributeNotSupported<'a> { + #[primary_span] + pub attr_span: Span, + pub attr_name: &'a str, + pub eii_name: String, + #[label("`#[{$eii_name}]` is not allowed to have `#[{$attr_name}]`")] + pub eii_span: Span, +} + #[derive(Diagnostic)] #[diag("`self` parameter is only allowed in associated functions")] #[note("associated functions are those in `impl` or `trait` definitions")] diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index bf2dd237cd797..82a55d4be3dce 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -773,22 +773,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { sig_span: sig.span, }); } - - if let Some(impls) = find_attr!(attrs, EiiImpls(impls) => impls) { - let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap(); - for i in impls { - let name = match i.resolution { - EiiImplResolution::Macro(def_id) => self.tcx.item_name(def_id), - EiiImplResolution::Known(decl) => decl.name.name, - EiiImplResolution::Error(_eg) => continue, - }; - self.dcx().emit_err(diagnostics::EiiWithTrackCaller { - attr_span, - name, - sig_span: sig.span, - }); - } - } } _ => {} } diff --git a/compiler/rustc_passes/src/diagnostics.rs b/compiler/rustc_passes/src/diagnostics.rs index 2589941d7dae1..860cc435db330 100644 --- a/compiler/rustc_passes/src/diagnostics.rs +++ b/compiler/rustc_passes/src/diagnostics.rs @@ -1077,16 +1077,6 @@ pub(crate) struct EiiImplRequiresUnsafeSuggestion { pub right: Span, } -#[derive(Diagnostic)] -#[diag("`#[{$name}]` is not allowed to have `#[track_caller]`")] -pub(crate) struct EiiWithTrackCaller { - #[primary_span] - pub attr_span: Span, - pub name: Symbol, - #[label("`#[{$name}]` is not allowed to have `#[track_caller]`")] - pub sig_span: Span, -} - #[derive(Diagnostic)] #[diag("`#[{$name}]` {$kind} required, but not found")] pub(crate) struct EiiWithoutImpl { diff --git a/tests/ui/eii/implementation-attribute-allowlist-issue-159015.rs b/tests/ui/eii/implementation-attribute-allowlist-issue-159015.rs new file mode 100644 index 0000000000000..ea73a972c9bb1 --- /dev/null +++ b/tests/ui/eii/implementation-attribute-allowlist-issue-159015.rs @@ -0,0 +1,91 @@ +// EII implementations only accept attributes from a conservative allowlist. +// Regression test for #159015 + +//@ edition: 2024 + +#![feature(coverage_attribute)] +#![feature(extern_item_impls)] +#![feature(optimize_attribute)] +#![feature(sanitize)] + +#[eii] +fn allowed(); + +/// Sugared and explicit documentation attributes are both allowed. +#[allowed] +#[allow(dead_code)] +#[warn(unreachable_code)] +#[deny(unused_mut)] +#[forbid(unsafe_code)] +#[expect(unused_variables)] +#[cfg(all())] +#[doc = "An allowed EII implementation."] +#[cold] +#[optimize(none)] +#[coverage(off)] +#[sanitize(address = "off")] +#[must_use] +#[deprecated] +fn allowed_impl() { + let unused = (); +} + +#[eii] +fn allowed_inline(); + +#[allowed_inline] +#[allow(unused_attributes)] +#[cfg_attr(all(), inline)] +fn allowed_inline_impl() {} + +#[eii] +fn foo(); + +#[foo] +#[unsafe(no_mangle)] +//~^ ERROR `#[foo]` is not allowed to have `#[no_mangle]` +fn bar() {} + +#[eii] +fn baz(); + +#[baz] +#[unsafe(export_name = "qux")] +//~^ ERROR `#[baz]` is not allowed to have `#[export_name]` +fn qux() {} + +#[eii] +fn quux(); + +#[quux] +#[unsafe(link_section = "__TEXT,__text")] +//~^ ERROR `#[quux]` is not allowed to have `#[link_section]` +fn corge() {} + +#[eii] +fn grault(); + +#[grault] +#[track_caller] +//~^ ERROR `#[grault]` is not allowed to have `#[track_caller]` +fn garply() {} + +#[eii] +fn multiple_invalid_attrs(); + +#[multiple_invalid_attrs] +#[unsafe(no_mangle)] +//~^ ERROR `#[multiple_invalid_attrs]` is not allowed to have `#[no_mangle]` +#[track_caller] +//~^ ERROR `#[multiple_invalid_attrs]` is not allowed to have `#[track_caller]` +fn multiple_invalid_attrs_impl() {} + +#[eii(static_eii)] +static STATIC_EII: u8; + +#[static_eii] +#[used] +//~^ ERROR `#[static_eii]` is not allowed to have `#[used]` +static STATIC_EII_IMPL: u8 = 0; + +fn main() {} diff --git a/tests/ui/eii/implementation-attribute-allowlist-issue-159015.stderr b/tests/ui/eii/implementation-attribute-allowlist-issue-159015.stderr new file mode 100644 index 0000000000000..5e67b0bc97f96 --- /dev/null +++ b/tests/ui/eii/implementation-attribute-allowlist-issue-159015.stderr @@ -0,0 +1,59 @@ +error: `#[foo]` is not allowed to have `#[no_mangle]` + --> $DIR/implementation-attribute-allowlist-issue-159015.rs:45:1 + | +LL | #[foo] + | ------ `#[foo]` is not allowed to have `#[no_mangle]` +LL | #[unsafe(no_mangle)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: `#[baz]` is not allowed to have `#[export_name]` + --> $DIR/implementation-attribute-allowlist-issue-159015.rs:53:1 + | +LL | #[baz] + | ------ `#[baz]` is not allowed to have `#[export_name]` +LL | #[unsafe(export_name = "qux")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `#[quux]` is not allowed to have `#[link_section]` + --> $DIR/implementation-attribute-allowlist-issue-159015.rs:61:1 + | +LL | #[quux] + | ------- `#[quux]` is not allowed to have `#[link_section]` +LL | #[unsafe(link_section = "__TEXT,__text")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `#[grault]` is not allowed to have `#[track_caller]` + --> $DIR/implementation-attribute-allowlist-issue-159015.rs:69:1 + | +LL | #[grault] + | --------- `#[grault]` is not allowed to have `#[track_caller]` +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ + +error: `#[multiple_invalid_attrs]` is not allowed to have `#[no_mangle]` + --> $DIR/implementation-attribute-allowlist-issue-159015.rs:77:1 + | +LL | #[multiple_invalid_attrs] + | ------------------------- `#[multiple_invalid_attrs]` is not allowed to have `#[no_mangle]` +LL | #[unsafe(no_mangle)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: `#[multiple_invalid_attrs]` is not allowed to have `#[track_caller]` + --> $DIR/implementation-attribute-allowlist-issue-159015.rs:79:1 + | +LL | #[multiple_invalid_attrs] + | ------------------------- `#[multiple_invalid_attrs]` is not allowed to have `#[track_caller]` +... +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ + +error: `#[static_eii]` is not allowed to have `#[used]` + --> $DIR/implementation-attribute-allowlist-issue-159015.rs:87:1 + | +LL | #[static_eii] + | ------------- `#[static_eii]` is not allowed to have `#[used]` +LL | #[used] + | ^^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/tests/ui/eii/track_caller_errors.stderr b/tests/ui/eii/track_caller_errors.stderr index e096146b67830..356f86093d638 100644 --- a/tests/ui/eii/track_caller_errors.stderr +++ b/tests/ui/eii/track_caller_errors.stderr @@ -4,8 +4,7 @@ error: `#[decl1]` is not allowed to have `#[track_caller]` LL | #[track_caller] | ^^^^^^^^^^^^^^^ LL | #[decl1] -LL | fn impl1(x: u64) { - | ---------------- `#[decl1]` is not allowed to have `#[track_caller]` + | -------- `#[decl1]` is not allowed to have `#[track_caller]` error: aborting due to 1 previous error