Auto merge of #126761 - GuillaumeGomez:unsafe_extern_blocks, r=spastorino

rustdoc: Add support for `missing_unsafe_on_extern` feature

Follow-up of https://github.com/rust-lang/rust/pull/124482.

Not sure if the `safe` keyword is supposed to be displayed or not though? For now I didn't add it in the generated doc, only `unsafe` as usual.

cc `@spastorino`
r? `@fmease`
This commit is contained in:
bors 2024-06-22 20:59:00 +00:00
commit 3cb521a434
7 changed files with 70 additions and 28 deletions

View File

@ -3077,9 +3077,7 @@ fn clean_maybe_renamed_foreign_item<'tcx>(
let def_id = item.owner_id.to_def_id(); let def_id = item.owner_id.to_def_id();
cx.with_param_env(def_id, |cx| { cx.with_param_env(def_id, |cx| {
let kind = match item.kind { let kind = match item.kind {
// FIXME(missing_unsafe_on_extern) handle safety of foreign fns. hir::ForeignItemKind::Fn(decl, names, generics, safety) => {
// Safety was added as part of the implementation of unsafe extern blocks PR #124482
hir::ForeignItemKind::Fn(decl, names, generics, _) => {
let (generics, decl) = enter_impl_trait(cx, |cx| { let (generics, decl) = enter_impl_trait(cx, |cx| {
// NOTE: generics must be cleaned before args // NOTE: generics must be cleaned before args
let generics = clean_generics(generics, cx); let generics = clean_generics(generics, cx);
@ -3087,13 +3085,12 @@ fn clean_maybe_renamed_foreign_item<'tcx>(
let decl = clean_fn_decl_with_args(cx, decl, None, args); let decl = clean_fn_decl_with_args(cx, decl, None, args);
(generics, decl) (generics, decl)
}); });
ForeignFunctionItem(Box::new(Function { decl, generics })) ForeignFunctionItem(Box::new(Function { decl, generics }), safety)
}
// FIXME(missing_unsafe_on_extern) handle safety of foreign statics.
// Safety was added as part of the implementation of unsafe extern blocks PR #124482
hir::ForeignItemKind::Static(ty, mutability, _) => {
ForeignStaticItem(Static { type_: clean_ty(ty, cx), mutability, expr: None })
} }
hir::ForeignItemKind::Static(ty, mutability, safety) => ForeignStaticItem(
Static { type_: clean_ty(ty, cx), mutability, expr: None },
safety,
),
hir::ForeignItemKind::Type => ForeignTypeItem, hir::ForeignItemKind::Type => ForeignTypeItem,
}; };

View File

@ -639,14 +639,14 @@ impl Item {
hir::FnHeader { safety: sig.safety(), abi: sig.abi(), constness, asyncness } hir::FnHeader { safety: sig.safety(), abi: sig.abi(), constness, asyncness }
} }
let header = match *self.kind { let header = match *self.kind {
ItemKind::ForeignFunctionItem(_) => { ItemKind::ForeignFunctionItem(_, safety) => {
let def_id = self.def_id().unwrap(); let def_id = self.def_id().unwrap();
let abi = tcx.fn_sig(def_id).skip_binder().abi(); let abi = tcx.fn_sig(def_id).skip_binder().abi();
hir::FnHeader { hir::FnHeader {
safety: if abi == Abi::RustIntrinsic { safety: if abi == Abi::RustIntrinsic {
intrinsic_operation_unsafety(tcx, def_id.expect_local()) intrinsic_operation_unsafety(tcx, def_id.expect_local())
} else { } else {
hir::Safety::Unsafe safety
}, },
abi, abi,
constness: if tcx.is_const_fn(def_id) constness: if tcx.is_const_fn(def_id)
@ -842,9 +842,9 @@ pub(crate) enum ItemKind {
StructFieldItem(Type), StructFieldItem(Type),
VariantItem(Variant), VariantItem(Variant),
/// `fn`s from an extern block /// `fn`s from an extern block
ForeignFunctionItem(Box<Function>), ForeignFunctionItem(Box<Function>, hir::Safety),
/// `static`s from an extern block /// `static`s from an extern block
ForeignStaticItem(Static), ForeignStaticItem(Static, hir::Safety),
/// `type`s from an extern block /// `type`s from an extern block
ForeignTypeItem, ForeignTypeItem,
MacroItem(Macro), MacroItem(Macro),
@ -893,8 +893,8 @@ impl ItemKind {
| TyMethodItem(_) | TyMethodItem(_)
| MethodItem(_, _) | MethodItem(_, _)
| StructFieldItem(_) | StructFieldItem(_)
| ForeignFunctionItem(_) | ForeignFunctionItem(_, _)
| ForeignStaticItem(_) | ForeignStaticItem(_, _)
| ForeignTypeItem | ForeignTypeItem
| MacroItem(_) | MacroItem(_)
| ProcMacroItem(_) | ProcMacroItem(_)
@ -924,8 +924,8 @@ impl ItemKind {
| StaticItem(_) | StaticItem(_)
| ConstantItem(_, _, _) | ConstantItem(_, _, _)
| TraitAliasItem(_) | TraitAliasItem(_)
| ForeignFunctionItem(_) | ForeignFunctionItem(_, _)
| ForeignStaticItem(_) | ForeignStaticItem(_, _)
| ForeignTypeItem | ForeignTypeItem
| MacroItem(_) | MacroItem(_)
| ProcMacroItem(_) | ProcMacroItem(_)

View File

@ -84,8 +84,8 @@ pub(crate) trait DocFolder: Sized {
| TyMethodItem(_) | TyMethodItem(_)
| MethodItem(_, _) | MethodItem(_, _)
| StructFieldItem(_) | StructFieldItem(_)
| ForeignFunctionItem(_) | ForeignFunctionItem(..)
| ForeignStaticItem(_) | ForeignStaticItem(..)
| ForeignTypeItem | ForeignTypeItem
| MacroItem(_) | MacroItem(_)
| ProcMacroItem(_) | ProcMacroItem(_)

View File

@ -254,7 +254,7 @@ pub(super) fn print_item(cx: &mut Context<'_>, item: &clean::Item, buf: &mut Buf
match &*item.kind { match &*item.kind {
clean::ModuleItem(ref m) => item_module(buf, cx, item, &m.items), clean::ModuleItem(ref m) => item_module(buf, cx, item, &m.items),
clean::FunctionItem(ref f) | clean::ForeignFunctionItem(ref f) => { clean::FunctionItem(ref f) | clean::ForeignFunctionItem(ref f, _) => {
item_function(buf, cx, item, f) item_function(buf, cx, item, f)
} }
clean::TraitItem(ref t) => item_trait(buf, cx, item, t), clean::TraitItem(ref t) => item_trait(buf, cx, item, t),
@ -265,7 +265,8 @@ pub(super) fn print_item(cx: &mut Context<'_>, item: &clean::Item, buf: &mut Buf
clean::MacroItem(ref m) => item_macro(buf, cx, item, m), clean::MacroItem(ref m) => item_macro(buf, cx, item, m),
clean::ProcMacroItem(ref m) => item_proc_macro(buf, cx, item, m), clean::ProcMacroItem(ref m) => item_proc_macro(buf, cx, item, m),
clean::PrimitiveItem(_) => item_primitive(buf, cx, item), clean::PrimitiveItem(_) => item_primitive(buf, cx, item),
clean::StaticItem(ref i) | clean::ForeignStaticItem(ref i) => item_static(buf, cx, item, i), clean::StaticItem(ref i) => item_static(buf, cx, item, i, None),
clean::ForeignStaticItem(ref i, safety) => item_static(buf, cx, item, i, Some(*safety)),
clean::ConstantItem(generics, ty, c) => item_constant(buf, cx, item, generics, ty, c), clean::ConstantItem(generics, ty, c) => item_constant(buf, cx, item, generics, ty, c),
clean::ForeignTypeItem => item_foreign_type(buf, cx, item), clean::ForeignTypeItem => item_foreign_type(buf, cx, item),
clean::KeywordItem => item_keyword(buf, cx, item), clean::KeywordItem => item_keyword(buf, cx, item),
@ -491,11 +492,14 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
} }
let unsafety_flag = match *myitem.kind { let unsafety_flag = match *myitem.kind {
clean::FunctionItem(_) | clean::ForeignFunctionItem(_) clean::FunctionItem(_) | clean::ForeignFunctionItem(..)
if myitem.fn_header(tcx).unwrap().safety == hir::Safety::Unsafe => if myitem.fn_header(tcx).unwrap().safety == hir::Safety::Unsafe =>
{ {
"<sup title=\"unsafe function\">⚠</sup>" "<sup title=\"unsafe function\">⚠</sup>"
} }
clean::ForeignStaticItem(_, hir::Safety::Unsafe) => {
"<sup title=\"unsafe static\">⚠</sup>"
}
_ => "", _ => "",
}; };
@ -1957,13 +1961,22 @@ fn item_fields(
} }
} }
fn item_static(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Static) { fn item_static(
w: &mut impl fmt::Write,
cx: &mut Context<'_>,
it: &clean::Item,
s: &clean::Static,
safety: Option<hir::Safety>,
) {
wrap_item(w, |buffer| { wrap_item(w, |buffer| {
render_attributes_in_code(buffer, it, cx); render_attributes_in_code(buffer, it, cx);
write!( write!(
buffer, buffer,
"{vis}static {mutability}{name}: {typ}", "{vis}{safe}static {mutability}{name}: {typ}",
vis = visibility_print_with_space(it, cx), vis = visibility_print_with_space(it, cx),
safe = safety
.map(|safe| if safe == hir::Safety::Unsafe { "unsafe " } else { "" })
.unwrap_or(""),
mutability = s.mutability.print_with_space(), mutability = s.mutability.print_with_space(),
name = it.name.unwrap(), name = it.name.unwrap(),
typ = s.type_.print(cx) typ = s.type_.print(cx)

View File

@ -310,14 +310,16 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
EnumItem(e) => ItemEnum::Enum(e.into_tcx(tcx)), EnumItem(e) => ItemEnum::Enum(e.into_tcx(tcx)),
VariantItem(v) => ItemEnum::Variant(v.into_tcx(tcx)), VariantItem(v) => ItemEnum::Variant(v.into_tcx(tcx)),
FunctionItem(f) => ItemEnum::Function(from_function(f, true, header.unwrap(), tcx)), FunctionItem(f) => ItemEnum::Function(from_function(f, true, header.unwrap(), tcx)),
ForeignFunctionItem(f) => ItemEnum::Function(from_function(f, false, header.unwrap(), tcx)), ForeignFunctionItem(f, _) => {
ItemEnum::Function(from_function(f, false, header.unwrap(), tcx))
}
TraitItem(t) => ItemEnum::Trait((*t).into_tcx(tcx)), TraitItem(t) => ItemEnum::Trait((*t).into_tcx(tcx)),
TraitAliasItem(t) => ItemEnum::TraitAlias(t.into_tcx(tcx)), TraitAliasItem(t) => ItemEnum::TraitAlias(t.into_tcx(tcx)),
MethodItem(m, _) => ItemEnum::Function(from_function(m, true, header.unwrap(), tcx)), MethodItem(m, _) => ItemEnum::Function(from_function(m, true, header.unwrap(), tcx)),
TyMethodItem(m) => ItemEnum::Function(from_function(m, false, header.unwrap(), tcx)), TyMethodItem(m) => ItemEnum::Function(from_function(m, false, header.unwrap(), tcx)),
ImplItem(i) => ItemEnum::Impl((*i).into_tcx(tcx)), ImplItem(i) => ItemEnum::Impl((*i).into_tcx(tcx)),
StaticItem(s) => ItemEnum::Static(s.into_tcx(tcx)), StaticItem(s) => ItemEnum::Static(s.into_tcx(tcx)),
ForeignStaticItem(s) => ItemEnum::Static(s.into_tcx(tcx)), ForeignStaticItem(s, _) => ItemEnum::Static(s.into_tcx(tcx)),
ForeignTypeItem => ItemEnum::ForeignType, ForeignTypeItem => ItemEnum::ForeignType,
TypeAliasItem(t) => ItemEnum::TypeAlias(t.into_tcx(tcx)), TypeAliasItem(t) => ItemEnum::TypeAlias(t.into_tcx(tcx)),
OpaqueTyItem(t) => ItemEnum::OpaqueTy(t.into_tcx(tcx)), OpaqueTyItem(t) => ItemEnum::OpaqueTy(t.into_tcx(tcx)),

View File

@ -33,8 +33,8 @@ pub(crate) trait DocVisitor: Sized {
| TyMethodItem(_) | TyMethodItem(_)
| MethodItem(_, _) | MethodItem(_, _)
| StructFieldItem(_) | StructFieldItem(_)
| ForeignFunctionItem(_) | ForeignFunctionItem(..)
| ForeignStaticItem(_) | ForeignStaticItem(..)
| ForeignTypeItem | ForeignTypeItem
| MacroItem(_) | MacroItem(_)
| ProcMacroItem(_) | ProcMacroItem(_)

View File

@ -0,0 +1,30 @@
// Test to ensure the feature is working as expected.
#![feature(unsafe_extern_blocks)]
#![crate_name = "foo"]
// @has 'foo/index.html'
// First we check that both the static and the function have a "sup" element
// to tell they're unsafe.
// @count - '//ul[@class="item-table"]//sup[@title="unsafe static"]' 1
// @has - '//ul[@class="item-table"]//sup[@title="unsafe static"]' '⚠'
// @count - '//ul[@class="item-table"]//sup[@title="unsafe function"]' 1
// @has - '//ul[@class="item-table"]//sup[@title="unsafe function"]' '⚠'
unsafe extern {
// @has 'foo/static.FOO.html'
// @has - '//pre[@class="rust item-decl"]' 'pub static FOO: i32'
pub safe static FOO: i32;
// @has 'foo/static.BAR.html'
// @has - '//pre[@class="rust item-decl"]' 'pub unsafe static BAR: i32'
pub static BAR: i32;
// @has 'foo/fn.foo.html'
// @has - '//pre[@class="rust item-decl"]' 'pub extern "C" fn foo()'
pub safe fn foo();
// @has 'foo/fn.bar.html'
// @has - '//pre[@class="rust item-decl"]' 'pub unsafe extern "C" fn bar()'
pub fn bar();
}