rustdoc: Create SelfTy to replace Generic(kw::SelfUpper)

Rustdoc often has to special-case `Self` because it is, well, a special
type of generic parameter (although it also behaves as an alias in
concrete impls). Instead of spreading this special-casing throughout the
code base, create a new variant of the `clean::Type` enum that is for
`Self` types.

This is a refactoring that has almost no impact on rustdoc's behavior,
except that `&Self`, `(Self,)`, `&[Self]`, and other similar occurrences
of `Self` no longer link to the wrapping type (reference primitive,
tuple primitive, etc.) as regular generics do. I felt this made more
sense since users would expect `Self` to link to the containing trait or
aliased type (though those are usually expanded), not the primitive that
is wrapping it. For an example of the change, see the docs for
`std::alloc::Allocator::by_ref`.
This commit is contained in:
Noah Lev 2024-07-31 16:45:05 -07:00
parent 249d686c70
commit 664b3ffbe9
8 changed files with 35 additions and 25 deletions

View File

@ -12,7 +12,7 @@ use rustc_middle::ty::fast_reject::SimplifiedType;
use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::ty::{self, TyCtxt};
use rustc_span::def_id::LOCAL_CRATE; use rustc_span::def_id::LOCAL_CRATE;
use rustc_span::hygiene::MacroKind; use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::symbol::{sym, Symbol};
use thin_vec::{thin_vec, ThinVec}; use thin_vec::{thin_vec, ThinVec};
use {rustc_ast as ast, rustc_hir as hir}; use {rustc_ast as ast, rustc_hir as hir};
@ -792,11 +792,7 @@ fn build_macro(
fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean::Generics { fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean::Generics {
for pred in &mut g.where_predicates { for pred in &mut g.where_predicates {
match *pred { match *pred {
clean::WherePredicate::BoundPredicate { clean::WherePredicate::BoundPredicate { ty: clean::SelfTy, ref mut bounds, .. } => {
ty: clean::Generic(ref s),
ref mut bounds,
..
} if *s == kw::SelfUpper => {
bounds.retain(|bound| match bound { bounds.retain(|bound| match bound {
clean::GenericBound::TraitBound(clean::PolyTrait { trait_, .. }, _) => { clean::GenericBound::TraitBound(clean::PolyTrait { trait_, .. }, _) => {
trait_.def_id() != trait_did trait_.def_id() != trait_did
@ -812,13 +808,13 @@ fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean:
clean::WherePredicate::BoundPredicate { clean::WherePredicate::BoundPredicate {
ty: ty:
clean::QPath(box clean::QPathData { clean::QPath(box clean::QPathData {
self_type: clean::Generic(ref s), self_type: clean::Generic(_),
trait_: Some(trait_), trait_: Some(trait_),
.. ..
}), }),
bounds, bounds,
.. ..
} => !(bounds.is_empty() || *s == kw::SelfUpper && trait_.def_id() == trait_did), } => !bounds.is_empty() && trait_.def_id() != trait_did,
_ => true, _ => true,
}); });
g g
@ -832,9 +828,7 @@ fn separate_supertrait_bounds(
) -> (clean::Generics, Vec<clean::GenericBound>) { ) -> (clean::Generics, Vec<clean::GenericBound>) {
let mut ty_bounds = Vec::new(); let mut ty_bounds = Vec::new();
g.where_predicates.retain(|pred| match *pred { g.where_predicates.retain(|pred| match *pred {
clean::WherePredicate::BoundPredicate { ty: clean::Generic(ref s), ref bounds, .. } clean::WherePredicate::BoundPredicate { ty: clean::SelfTy, ref bounds, .. } => {
if *s == kw::SelfUpper =>
{
ty_bounds.extend(bounds.iter().cloned()); ty_bounds.extend(bounds.iter().cloned());
false false
} }

View File

@ -1351,11 +1351,11 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
let self_arg_ty = let self_arg_ty =
tcx.fn_sig(assoc_item.def_id).instantiate_identity().input(0).skip_binder(); tcx.fn_sig(assoc_item.def_id).instantiate_identity().input(0).skip_binder();
if self_arg_ty == self_ty { if self_arg_ty == self_ty {
item.decl.inputs.values[0].type_ = Generic(kw::SelfUpper); item.decl.inputs.values[0].type_ = SelfTy;
} else if let ty::Ref(_, ty, _) = *self_arg_ty.kind() { } else if let ty::Ref(_, ty, _) = *self_arg_ty.kind() {
if ty == self_ty { if ty == self_ty {
match item.decl.inputs.values[0].type_ { match item.decl.inputs.values[0].type_ {
BorrowedRef { ref mut type_, .. } => **type_ = Generic(kw::SelfUpper), BorrowedRef { ref mut type_, .. } => **type_ = SelfTy,
_ => unreachable!(), _ => unreachable!(),
} }
} }
@ -1439,9 +1439,8 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
if trait_.def_id() != assoc_item.container_id(tcx) { if trait_.def_id() != assoc_item.container_id(tcx) {
return true; return true;
} }
match *self_type { if *self_type != SelfTy {
Generic(ref s) if *s == kw::SelfUpper => {} return true;
_ => return true,
} }
match &assoc.args { match &assoc.args {
GenericArgs::AngleBracketed { args, constraints } => { GenericArgs::AngleBracketed { args, constraints } => {
@ -2228,6 +2227,8 @@ pub(crate) fn clean_middle_ty<'tcx>(
ty::Param(ref p) => { ty::Param(ref p) => {
if let Some(bounds) = cx.impl_trait_bounds.remove(&p.index.into()) { if let Some(bounds) = cx.impl_trait_bounds.remove(&p.index.into()) {
ImplTrait(bounds) ImplTrait(bounds)
} else if p.name == kw::SelfUpper {
SelfTy
} else { } else {
Generic(p.name) Generic(p.name)
} }

View File

@ -145,7 +145,6 @@ pub(crate) fn sized_bounds(cx: &mut DocContext<'_>, generics: &mut clean::Generi
// should be handled when cleaning associated types. // should be handled when cleaning associated types.
generics.where_predicates.retain(|pred| { generics.where_predicates.retain(|pred| {
if let WP::BoundPredicate { ty: clean::Generic(param), bounds, .. } = pred if let WP::BoundPredicate { ty: clean::Generic(param), bounds, .. } = pred
&& *param != rustc_span::symbol::kw::SelfUpper
&& bounds.iter().any(|b| b.is_sized_bound(cx)) && bounds.iter().any(|b| b.is_sized_bound(cx))
{ {
sized_params.insert(*param); sized_params.insert(*param);

View File

@ -37,7 +37,7 @@ pub(crate) use self::ItemKind::*;
pub(crate) use self::ReceiverTy::*; pub(crate) use self::ReceiverTy::*;
pub(crate) use self::Type::{ pub(crate) use self::Type::{
Array, BareFunction, BorrowedRef, DynTrait, Generic, ImplTrait, Infer, Primitive, QPath, Array, BareFunction, BorrowedRef, DynTrait, Generic, ImplTrait, Infer, Primitive, QPath,
RawPointer, Slice, Tuple, RawPointer, SelfTy, Slice, Tuple,
}; };
use crate::clean::cfg::Cfg; use crate::clean::cfg::Cfg;
use crate::clean::clean_middle_path; use crate::clean::clean_middle_path;
@ -1477,6 +1477,8 @@ pub(crate) enum Type {
DynTrait(Vec<PolyTrait>, Option<Lifetime>), DynTrait(Vec<PolyTrait>, Option<Lifetime>),
/// A type parameter. /// A type parameter.
Generic(Symbol), Generic(Symbol),
/// The `Self` type.
SelfTy,
/// A primitive (aka, builtin) type. /// A primitive (aka, builtin) type.
Primitive(PrimitiveType), Primitive(PrimitiveType),
/// A function pointer: `extern "ABI" fn(...) -> ...` /// A function pointer: `extern "ABI" fn(...) -> ...`
@ -1571,6 +1573,8 @@ impl Type {
// If both sides are generic, this returns true. // If both sides are generic, this returns true.
(_, Type::Generic(_)) => true, (_, Type::Generic(_)) => true,
(Type::Generic(_), _) => false, (Type::Generic(_), _) => false,
// `Self` only matches itself.
(Type::SelfTy, Type::SelfTy) => true,
// Paths account for both the path itself and its generics. // Paths account for both the path itself and its generics.
(Type::Path { path: a }, Type::Path { path: b }) => { (Type::Path { path: a }, Type::Path { path: b }) => {
a.def_id() == b.def_id() a.def_id() == b.def_id()
@ -1642,7 +1646,7 @@ impl Type {
pub(crate) fn is_self_type(&self) -> bool { pub(crate) fn is_self_type(&self) -> bool {
match *self { match *self {
Generic(name) => name == kw::SelfUpper, SelfTy => true,
_ => false, _ => false,
} }
} }
@ -1700,7 +1704,7 @@ impl Type {
Type::Pat(..) => PrimitiveType::Pat, Type::Pat(..) => PrimitiveType::Pat,
RawPointer(..) => PrimitiveType::RawPointer, RawPointer(..) => PrimitiveType::RawPointer,
QPath(box QPathData { ref self_type, .. }) => return self_type.def_id(cache), QPath(box QPathData { ref self_type, .. }) => return self_type.def_id(cache),
Generic(_) | Infer | ImplTrait(_) => return None, Generic(_) | SelfTy | Infer | ImplTrait(_) => return None,
}; };
Primitive(t).def_id(cache) Primitive(t).def_id(cache)
} }

View File

@ -468,7 +468,7 @@ pub(crate) fn resolve_type(cx: &mut DocContext<'_>, path: Path) -> Type {
match path.res { match path.res {
Res::PrimTy(p) => Primitive(PrimitiveType::from(p)), Res::PrimTy(p) => Primitive(PrimitiveType::from(p)),
Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } if path.segments.len() == 1 => { Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } if path.segments.len() == 1 => {
Generic(kw::SelfUpper) Type::SelfTy
} }
Res::Def(DefKind::TyParam, _) if path.segments.len() == 1 => Generic(path.segments[0].name), Res::Def(DefKind::TyParam, _) if path.segments.len() == 1 => Generic(path.segments[0].name),
_ => { _ => {

View File

@ -1006,6 +1006,7 @@ fn fmt_type<'cx>(
match *t { match *t {
clean::Generic(name) => f.write_str(name.as_str()), clean::Generic(name) => f.write_str(name.as_str()),
clean::SelfTy => f.write_str("Self"),
clean::Type::Path { ref path } => { clean::Type::Path { ref path } => {
// Paths like `T::Output` and `Self::Output` should be rendered with all segments. // Paths like `T::Output` and `Self::Output` should be rendered with all segments.
let did = path.def_id(); let did = path.def_id();

View File

@ -797,7 +797,11 @@ fn get_index_type_id(
} }
} }
// Not supported yet // Not supported yet
clean::Type::Pat(..) | clean::Generic(_) | clean::ImplTrait(_) | clean::Infer => None, clean::Type::Pat(..)
| clean::Generic(_)
| clean::SelfTy
| clean::ImplTrait(_)
| clean::Infer => None,
} }
} }
@ -848,13 +852,18 @@ fn simplify_fn_type<'tcx, 'a>(
(false, arg) (false, arg)
}; };
let as_arg_s = |t: &Type| match *t {
Type::Generic(arg_s) => Some(arg_s),
Type::SelfTy => Some(kw::SelfUpper),
_ => None,
};
// If this argument is a type parameter and not a trait bound or a type, we need to look // If this argument is a type parameter and not a trait bound or a type, we need to look
// for its bounds. // for its bounds.
if let Type::Generic(arg_s) = *arg { if let Some(arg_s) = as_arg_s(arg) {
// First we check if the bounds are in a `where` predicate... // First we check if the bounds are in a `where` predicate...
let mut type_bounds = Vec::new(); let mut type_bounds = Vec::new();
for where_pred in generics.where_predicates.iter().filter(|g| match g { for where_pred in generics.where_predicates.iter().filter(|g| match g {
WherePredicate::BoundPredicate { ty: Type::Generic(ty_s), .. } => *ty_s == arg_s, WherePredicate::BoundPredicate { ty, .. } => *ty == *arg,
_ => false, _ => false,
}) { }) {
let bounds = where_pred.get_bounds().unwrap_or_else(|| &[]); let bounds = where_pred.get_bounds().unwrap_or_else(|| &[]);

View File

@ -578,7 +578,7 @@ impl FromWithTcx<clean::Type> for Type {
fn from_tcx(ty: clean::Type, tcx: TyCtxt<'_>) -> Self { fn from_tcx(ty: clean::Type, tcx: TyCtxt<'_>) -> Self {
use clean::Type::{ use clean::Type::{
Array, BareFunction, BorrowedRef, Generic, ImplTrait, Infer, Primitive, QPath, Array, BareFunction, BorrowedRef, Generic, ImplTrait, Infer, Primitive, QPath,
RawPointer, Slice, Tuple, RawPointer, SelfTy, Slice, Tuple,
}; };
match ty { match ty {
@ -588,6 +588,8 @@ impl FromWithTcx<clean::Type> for Type {
traits: bounds.into_tcx(tcx), traits: bounds.into_tcx(tcx),
}), }),
Generic(s) => Type::Generic(s.to_string()), Generic(s) => Type::Generic(s.to_string()),
// FIXME: add dedicated variant to json Type?
SelfTy => Type::Generic("Self".to_owned()),
Primitive(p) => Type::Primitive(p.as_sym().to_string()), Primitive(p) => Type::Primitive(p.as_sym().to_string()),
BareFunction(f) => Type::FunctionPointer(Box::new((*f).into_tcx(tcx))), BareFunction(f) => Type::FunctionPointer(Box::new((*f).into_tcx(tcx))),
Tuple(t) => Type::Tuple(t.into_tcx(tcx)), Tuple(t) => Type::Tuple(t.into_tcx(tcx)),