diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 09b2bc5dcef1d..e6a1b7af19f90 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -583,7 +583,9 @@ pub(crate) fn build_impl( }; let trait_ = associated_trait .map(|t| clean_trait_ref_with_constraints(cx, ty::Binder::dummy(t), ThinVec::new())); - if trait_.as_ref().map(|t| t.def_id()) == tcx.lang_items().deref_trait() { + if trait_.as_ref().map(|t| t.def_id()) == tcx.lang_items().deref_trait() + && polarity != ty::ImplPolarity::Negative + { super::build_deref_target_impls(cx, &trait_items, ret); } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index f54339429fa58..27a1236e18bd9 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2941,9 +2941,11 @@ fn clean_impl<'tcx>( .map(|&ii| clean_impl_item(tcx.hir_impl_item(ii), cx)) .collect::>(); - // If this impl block is an implementation of the Deref trait, then we + // If this impl block is a positive implementation of the Deref trait, then we // need to try inlining the target's inherent impl blocks as well. - if trait_.as_ref().map(|t| t.def_id()) == tcx.lang_items().deref_trait() { + if trait_.as_ref().is_some_and(|t| tcx.lang_items().deref_trait() == Some(t.def_id())) + && tcx.impl_polarity(def_id) != ty::ImplPolarity::Negative + { build_deref_target_impls(cx, &items, &mut ret); } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 556d383a0e9fb..c0c380447f2cb 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1517,8 +1517,9 @@ fn render_assoc_items_inner( } if !traits.is_empty() { - let deref_impl = - traits.iter().find(|t| t.trait_did() == cx.tcx().lang_items().deref_trait()); + let deref_impl = traits.iter().find(|t| { + t.trait_did() == cx.tcx().lang_items().deref_trait() && !t.is_negative_trait_impl() + }); if let Some(impl_) = deref_impl { let has_deref_mut = traits.iter().any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait()); diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index a4535792ac3ce..472136ac46bcc 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -466,9 +466,9 @@ fn sidebar_assoc_items<'a>( ]; if v.iter().any(|i| i.inner_impl().trait_.is_some()) { - if let Some(impl_) = - v.iter().find(|i| i.trait_did() == cx.tcx().lang_items().deref_trait()) - { + if let Some(impl_) = v.iter().find(|i| { + i.trait_did() == cx.tcx().lang_items().deref_trait() && !i.is_negative_trait_impl() + }) { let mut derefs = DefIdSet::default(); derefs.insert(did); sidebar_deref_methods( @@ -579,6 +579,7 @@ fn sidebar_deref_methods<'a>( .as_ref() .map(|t| Some(t.def_id()) == cx.tcx().lang_items().deref_trait()) .unwrap_or(false) + && !i.is_negative_trait_impl() }) { sidebar_deref_methods( diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index b58daceed0702..251efa15e240a 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -155,8 +155,9 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> // scan through included items ahead of time to splice in Deref targets to the "valid" sets for it in new_items_external.iter().chain(new_items_local.iter()) { - if let ImplItem(box Impl { ref for_, ref trait_, ref items, .. }) = it.kind + if let ImplItem(box Impl { ref for_, ref trait_, ref items, polarity, .. }) = it.kind && trait_.as_ref().map(|t| t.def_id()) == tcx.lang_items().deref_trait() + && polarity != ty::ImplPolarity::Negative && cleaner.keep_impl(for_, true) { let target = items diff --git a/tests/rustdoc-html/deref/negative-deref-impl-128801.rs b/tests/rustdoc-html/deref/negative-deref-impl-128801.rs new file mode 100644 index 0000000000000..51be958c33bbb --- /dev/null +++ b/tests/rustdoc-html/deref/negative-deref-impl-128801.rs @@ -0,0 +1,23 @@ +#![feature(negative_impls)] +#![crate_name = "foo"] + +// Regression test for https://github.com/rust-lang/rust/issues/128801 +// Negative `Deref`/`DerefMut` impls should not cause an ICE and should still be rendered. + +pub struct Source; + +//@ has foo/struct.Source.html + +// Verify negative Deref impl is rendered in the main content. +//@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'impl !Deref for Source' + +// Verify negative DerefMut impl is rendered in the main content. +//@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'impl !DerefMut for Source' + +// Verify negative impls appear in the sidebar. +//@ has - '//div[@class="sidebar-elems"]//h3/a[@href="#trait-implementations"]' 'Trait Implementations' +//@ has - '//*[@class="sidebar-elems"]//section//a' '!Deref' +//@ has - '//*[@class="sidebar-elems"]//section//a' '!DerefMut' + +impl !std::ops::Deref for Source {} +impl !std::ops::DerefMut for Source {} diff --git a/tests/rustdoc-ui/deref/negative-deref-ice-128801.rs b/tests/rustdoc-ui/deref/negative-deref-ice-128801.rs new file mode 100644 index 0000000000000..439727f3d3d32 --- /dev/null +++ b/tests/rustdoc-ui/deref/negative-deref-ice-128801.rs @@ -0,0 +1,11 @@ +//@ check-pass + +// Regression test for https://github.com/rust-lang/rust/issues/128801 +// Negative `Deref`/`DerefMut` impls should not cause an ICE. + +#![feature(negative_impls)] + +pub struct Source; + +impl !std::ops::Deref for Source {} +impl !std::ops::DerefMut for Source {}