rust-lang/rust

unsafe(#[attribute]) not working

Opened this issue · 11 comments

I have this code

#[syscalls]
extern "C" {
    pub fn exit(code: i32) -> usize;
}

which fails to compile:

error: unsafe attribute used without unsafe
 --> crates/syscalls/src/lib.rs:9:1
  |
9 | #[syscalls]
  | ^^^^^^^^^^^ usage of unsafe attribute
  |
  = note: this error originates in the attribute macro `syscalls` (in Nightly builds, run with -Z macro-backtrace for more info)
help: wrap the attribute in `unsafe(...)`
  |
9 | unsafe(#[syscalls])
  | +++++++           +

so I try what it says:

unsafe(#[syscalls])
extern "C" {
    pub fn exit(code: i32) -> usize;
}

and get another compilation error:

error: expected item, found keyword `unsafe`
 --> crates/syscalls/src/lib.rs:9:1
  |
9 | unsafe(#[syscalls])
  | ^^^^^^ expected item

Meta

rustc --version --verbose:

rustc 1.92.0-nightly (dd7fda570 2025-09-20)
binary: rustc
commit-hash: dd7fda570040e8a736f7d8bc28ddd1b444aabc82
commit-date: 2025-09-20
host: x86_64-unknown-linux-gnu
release: 1.92.0-nightly
LLVM version: 21.1.1
Backtrace

<backtrace>

You need to write #[unsafe(syscalls)]

#[unsafe(syscalls)]
extern "C" {
    pub fn exit(code: i32) -> usize;
}

Doesn't work either:

error: unsafe attribute used without unsafe
 --> crates/syscalls/src/lib.rs:9:1
  |
9 | #[unsafe(syscalls)]
  | ^^^^^^^^^^^^^^^^^^^ usage of unsafe attribute
  |
  = note: this error originates in the attribute macro `syscalls` (in Nightly builds, run with -Z macro-backtrace for more info)
help: wrap the attribute in `unsafe(...)`
  |
9 | unsafe(#[unsafe(syscalls)])
  | +++++++                   +

The syscalls proc-macro needs to be updated to support the 2024 edition. Which crate did you get it from?

The syscalls proc-macro needs to be updated to support the 2024 edition.

Even if the consuming crate isn't using 2024 edition?

Which crate did you get it from?

It's my own: https://github.com/kjughx/ruix/tree/master/crates/syscalls

The syscalls proc-macro needs to be updated to support the 2024 edition

Seems like the issue is what the proc-macro actually generates. It generates code with unsafe attributes. The compilation error message is quite misleading.

Okay, so the suggestion is obviously invalid.

However, shouldn't a Rust 2021 proc macro attribute expanding to an "unsafe in Rust >=2024" attribute be legal without being marked unsafe(…) at the usage site regardless of the target crate's edition (if that's what it boils down to)?

Similar to how the following is allowed:

//-- e15.rs --
//@ edition: 2015
#[macro_export]
macro_rules! make {
    () => { fn async() {} }
}

//-- e24.rs --
//@ edition: 2024
e15::make!();

fn main() {
    r#async();
}

The code above compiles even though there's an fn async() {} in a Rust 2024 crate because the span comes from a Rust 2015 crate.

if that's what it boils down to

Okay so far I couldn't minimize it to that, the span logic seems to work correctly for attributes...

This is as minimal as I could get it:

// -- e15.rs --
// @ edition: 2018/2021/2024

use quote::quote;
use proc_macro::TokenStream;

#[proc_macro_attribute]
pub fn attribute(_: TokenStream, _: TokenStream) -> TokenStream {
    TokenStream::from(
    quote! {
        #[naked]
        extern "C" fn function() {
            core::arch::naked_asm!("hlt")
        }
    })
}

// -- e24.rs --
// @ edition: 2018/2021/2024

#[e15::attribute]
fn function() {}

fn main() {}

Always:

   Compiling e15 v0.1.0 (/home/philip/triage/e15)
   Compiling e24 v0.1.0 (/home/philip/triage/e24)
error: unsafe attribute used without unsafe
 --> src/main.rs:1:1
  |
1 | #[e15::attribute]
  | ^^^^^^^^^^^^^^^^^ usage of unsafe attribute
  |
  = note: this error originates in the attribute macro `e15::attribute` (in Nightly builds, run with -Z macro-backtrace for more info)
help: wrap the attribute in `unsafe(...)`
  |
1 | unsafe(#[e15::attribute])
  | +++++++                 +

error: could not compile `e24` (bin "e24") due to 1 previous error

The naked attribute is unsafe in all editions given that it was only stabilized after we got support for unsafe attributes. So this is just a diagnostic bug.

I don't know what this means, but the issue is fixed when using

#![feature(proc_macro_quote)]
use proc_macro::{TokenStream, quote};

so, the issue appears to be that quote::quote emits the wrong span?

quote::quote emits mixed-site spans, like decl-macro 1.0 (or was it call-site spans?), while proc_macro::quote emits def-site spans, like decl-macro 2.0. Deg-site spans are unstable as the way they are supposed to behave hasn't been formalized yet.

But is there something that rustc can do here if it gets an incorrect span from the macro?