Rollup merge of #143662 - obi1kenobi:pg/unsafe-attribute-wrappers, r=t-rustdoc

[rustdoc] Display unsafe attrs with edition 2024 `unsafe()` wrappers.

Use Rust 2024 edition representation for unsafe attributes in rustdoc HTML:
- `#[no_mangle]` -> `#[unsafe(no_mangle)]`
- `#[export_name = "foo"]` -> `#[unsafe(export_name = "foo")]`
- `#[link_section = ".text"]` -> `#[unsafe(link_section = ".text")]`

The 2024 edition representation is used regardless of the crate's own edition. This ensures that Rustaceans don't have to learn the rules of an outdated edition (e.g. that `unsafe()` wasn't always necessary) in order to understand a crate's documentation.

After some looking through the `T-rustdoc` issues, I was not able to find an existing issue for this. Apologies if I missed it.

r? ``````@aDotInTheVoid``````
This commit is contained in:
Samuel Tardieu 2025-08-02 11:24:21 +02:00 committed by GitHub
commit 4eddebd42b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 46 additions and 14 deletions

View File

@ -763,13 +763,13 @@ impl Item {
.iter()
.filter_map(|attr| match attr {
hir::Attribute::Parsed(AttributeKind::LinkSection { name, .. }) => {
Some(format!("#[link_section = \"{name}\"]"))
Some(format!("#[unsafe(link_section = \"{name}\")]"))
}
hir::Attribute::Parsed(AttributeKind::NoMangle(..)) => {
Some("#[no_mangle]".to_string())
Some("#[unsafe(no_mangle)]".to_string())
}
hir::Attribute::Parsed(AttributeKind::ExportName { name, .. }) => {
Some(format!("#[export_name = \"{name}\"]"))
Some(format!("#[unsafe(export_name = \"{name}\")]"))
}
hir::Attribute::Parsed(AttributeKind::NonExhaustive(..)) => {
Some("#[non_exhaustive]".to_string())

View File

@ -1,7 +1,7 @@
#![crate_name = "foo"]
//@ has 'foo/fn.f.html'
//@ has - //*[@'class="rust item-decl"]' '#[export_name = "f"] pub fn f()'
#[export_name = "\
f"]
//@ has - //*[@'class="rust item-decl"]' '#[unsafe(export_name = "f")] pub fn f()'
#[unsafe(export_name = "\
f")]
pub fn f() {}

View File

@ -0,0 +1,14 @@
//@ edition: 2021
#![crate_name = "foo"]
//@ has foo/fn.f.html '//pre[@class="rust item-decl"]' '#[unsafe(no_mangle)]'
#[no_mangle]
pub extern "C" fn f() {}
//@ has foo/fn.g.html '//pre[@class="rust item-decl"]' '#[unsafe(export_name = "bar")]'
#[export_name = "bar"]
pub extern "C" fn g() {}
//@ has foo/fn.example.html '//pre[@class="rust item-decl"]' '#[unsafe(link_section = ".text")]'
#[link_section = ".text"]
pub extern "C" fn example() {}

View File

@ -0,0 +1,13 @@
// Tests that attributes are correctly copied onto a re-exported item.
//@ edition:2024
#![crate_name = "re_export"]
//@ has 're_export/fn.thingy2.html' '//pre[@class="rust item-decl"]' '#[unsafe(no_mangle)]'
pub use thingymod::thingy as thingy2;
mod thingymod {
#[unsafe(no_mangle)]
pub fn thingy() {
}
}

View File

@ -2,7 +2,7 @@
//@ edition:2021
#![crate_name = "re_export"]
//@ has 're_export/fn.thingy2.html' '//pre[@class="rust item-decl"]' '#[no_mangle]'
//@ has 're_export/fn.thingy2.html' '//pre[@class="rust item-decl"]' '#[unsafe(no_mangle)]'
pub use thingymod::thingy as thingy2;
mod thingymod {

View File

@ -1,13 +1,18 @@
//@ edition: 2024
#![crate_name = "foo"]
//@ has foo/fn.f.html '//pre[@class="rust item-decl"]' '#[no_mangle]'
#[no_mangle]
//@ has foo/fn.f.html '//pre[@class="rust item-decl"]' '#[unsafe(no_mangle)]'
#[unsafe(no_mangle)]
pub extern "C" fn f() {}
//@ has foo/fn.g.html '//pre[@class="rust item-decl"]' '#[export_name = "bar"]'
#[export_name = "bar"]
//@ has foo/fn.g.html '//pre[@class="rust item-decl"]' '#[unsafe(export_name = "bar")]'
#[unsafe(export_name = "bar")]
pub extern "C" fn g() {}
//@ has foo/fn.example.html '//pre[@class="rust item-decl"]' '#[unsafe(link_section = ".text")]'
#[unsafe(link_section = ".text")]
pub extern "C" fn example() {}
//@ has foo/struct.Repr.html '//pre[@class="rust item-decl"]' '#[repr(C, align(8))]'
#[repr(C, align(8))]
pub struct Repr;

View File

@ -4,13 +4,13 @@
extern crate reexports_attrs;
//@ has 'foo/fn.f0.html' '//pre[@class="rust item-decl"]' '#[no_mangle]'
//@ has 'foo/fn.f0.html' '//pre[@class="rust item-decl"]' '#[unsafe(no_mangle)]'
pub use reexports_attrs::f0;
//@ has 'foo/fn.f1.html' '//pre[@class="rust item-decl"]' '#[link_section = ".here"]'
//@ has 'foo/fn.f1.html' '//pre[@class="rust item-decl"]' '#[unsafe(link_section = ".here")]'
pub use reexports_attrs::f1;
//@ has 'foo/fn.f2.html' '//pre[@class="rust item-decl"]' '#[export_name = "f2export"]'
//@ has 'foo/fn.f2.html' '//pre[@class="rust item-decl"]' '#[unsafe(export_name = "f2export")]'
pub use reexports_attrs::f2;
//@ has 'foo/enum.T0.html' '//pre[@class="rust item-decl"]' '#[repr(u8)]'