Rollup merge of #154444 - jakubadamw:issue-128801, r=fmease

rustdoc ICE fix: When collecting `Deref` impls with their targets, skip the negative ones

rustdoc assumed every `Deref` impl has an associated `Target` type, but negative impls (e.g. `impl !Deref for T {}`) have none.

Skip them in both the trait-impl collection pass and the HTML render pass to avoid panicking on the missing `Target`.

Closes rust-lang/rust#128801.
This commit is contained in:
Jacob Pratt
2026-04-02 20:53:31 -04:00
committed by GitHub
7 changed files with 50 additions and 9 deletions
+3 -1
View File
@@ -580,7 +580,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);
}
+4 -2
View File
@@ -2942,9 +2942,11 @@ fn clean_impl<'tcx>(
.map(|&ii| clean_impl_item(tcx.hir_impl_item(ii), cx))
.collect::<Vec<_>>();
// 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);
}
+3 -2
View File
@@ -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());
+4 -3
View File
@@ -477,9 +477,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(
@@ -598,6 +598,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(
+2 -1
View File
@@ -155,8 +155,9 @@ fn add_deref_target(
// 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
@@ -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 {}
@@ -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 {}