Skip to content

rustdoc_json: improve handling of generic args #142502

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions src/librustdoc/json/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,13 @@ pub(crate) fn from_deprecation(deprecation: attrs::Deprecation) -> Deprecation {
Deprecation { since, note: note.map(|s| s.to_string()) }
}

impl FromClean<clean::GenericArgs> for GenericArgs {
impl FromClean<clean::GenericArgs> for Option<Box<GenericArgs>> {
fn from_clean(args: &clean::GenericArgs, renderer: &JsonRenderer<'_>) -> Self {
use clean::GenericArgs::*;
match args {
if args.is_empty() {
return None;
}
Some(Box::new(match args {
AngleBracketed { args, constraints } => GenericArgs::AngleBracketed {
args: args.into_json(renderer),
constraints: constraints.into_json(renderer),
Expand All @@ -183,7 +186,7 @@ impl FromClean<clean::GenericArgs> for GenericArgs {
output: output.as_ref().map(|a| a.as_ref().into_json(renderer)),
},
ReturnTypeNotation => GenericArgs::ReturnTypeNotation,
}
}))
}
}

Expand Down Expand Up @@ -580,7 +583,20 @@ impl FromClean<clean::Path> for Path {
Path {
path: path.whole_name(),
id: renderer.id_from_item_default(path.def_id().into()),
args: path.segments.last().map(|args| Box::new(args.args.into_json(renderer))),
args: {
if let Some((final_seg, rest_segs)) = path.segments.split_last() {
// In general, `clean::Path` can hold things like
// `std::vec::Vec::<u32>::new`, where generic args appear
// in a middle segment. But for the places where `Path` is
// used by rustdoc-json-types, generic args can only be
// used in the final segment, e.g. `std::vec::Vec<u32>`. So
// check that the non-final segments have no generic args.
assert!(rest_segs.iter().all(|seg| seg.args.is_empty()));
final_seg.args.into_json(renderer)
} else {
None // no generics on any segments because there are no segments
}
},
}
}
}
Expand All @@ -591,7 +607,7 @@ impl FromClean<clean::QPathData> for Type {

Self::QualifiedPath {
name: assoc.name.to_string(),
args: Box::new(assoc.args.into_json(renderer)),
args: assoc.args.into_json(renderer),
self_type: Box::new(self_type.into_json(renderer)),
trait_: trait_.as_ref().map(|trait_| trait_.into_json(renderer)),
}
Expand Down
14 changes: 7 additions & 7 deletions src/rustdoc-json-types/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ pub type FxHashMap<K, V> = HashMap<K, V>; // re-export for use in src/librustdoc
// will instead cause conflicts. See #94591 for more. (This paragraph and the "Latest feature" line
// are deliberately not in a doc comment, because they need not be in public docs.)
//
// Latest feature: rustdoc JSON: Don't apply #[repr] privacy heuristics
pub const FORMAT_VERSION: u32 = 46;
// Latest feature: improve handling of generic args
pub const FORMAT_VERSION: u32 = 47;

/// The root of the emitted JSON blob.
///
Expand Down Expand Up @@ -277,8 +277,8 @@ pub struct PolyTrait {
/// A set of generic arguments provided to a path segment, e.g.
///
/// ```text
/// std::option::Option::<u32>::None
/// ^^^^^
/// std::option::Option<u32>
/// ^^^^^
/// ```
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
Expand Down Expand Up @@ -331,7 +331,7 @@ pub enum GenericArg {
Const(Constant),
/// A generic argument that's explicitly set to be inferred.
/// ```text
/// std::vec::Vec::<_>::new()
/// std::vec::Vec::<_>
/// ^
/// ```
Infer,
Expand Down Expand Up @@ -362,7 +362,7 @@ pub struct AssocItemConstraint {
/// The name of the associated type/constant.
pub name: String,
/// Arguments provided to the associated type/constant.
pub args: GenericArgs,
pub args: Option<Box<GenericArgs>>,
/// The kind of bound applied to the associated type/constant.
pub binding: AssocItemConstraintKind,
}
Expand Down Expand Up @@ -1118,7 +1118,7 @@ pub enum Type {
/// <core::slice::IterMut<'static, u32> as BetterIterator>::Item<'static>
/// // ^^^^^^^^^
/// ```
args: Box<GenericArgs>,
args: Option<Box<GenericArgs>>,
/// The type with which this type is associated.
///
/// ```ignore (incomplete expression)
Expand Down
25 changes: 25 additions & 0 deletions src/rustdoc-json-types/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,28 @@ fn test_union_info_roundtrip() {
let decoded: ItemEnum = bincode::deserialize(&encoded).unwrap();
assert_eq!(u, decoded);
}

// The memory used by a `Crate` can get large. These types are the ones that
// contribute the most to its size.
#[test]
#[cfg(target_pointer_width = "64")]
fn test_type_sizes() {
// tidy-alphabetical-start
assert_eq!(size_of::<AssocItemConstraint>(), 112);
assert_eq!(size_of::<Crate>(), 184);
assert_eq!(size_of::<ExternalCrate>(), 48);
assert_eq!(size_of::<FunctionPointer>(), 168);
assert_eq!(size_of::<GenericArg>(), 80);
assert_eq!(size_of::<GenericArgs>(), 104);
assert_eq!(size_of::<GenericBound>(), 72);
assert_eq!(size_of::<GenericParamDef>(), 136);
assert_eq!(size_of::<Impl>(), 304);
assert_eq!(size_of::<Item>(), 552);
assert_eq!(size_of::<ItemSummary>(), 32);
assert_eq!(size_of::<PolyTrait>(), 64);
assert_eq!(size_of::<PreciseCapturingArg>(), 32);
assert_eq!(size_of::<TargetFeature>(), 80);
assert_eq!(size_of::<Type>(), 80);
assert_eq!(size_of::<WherePredicate>(), 160);
// tidy-alphabetical-end
}
13 changes: 6 additions & 7 deletions src/tools/jsondoclint/src/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ impl<'a> Validator<'a> {
Type::RawPointer { is_mutable: _, type_ } => self.check_type(&**type_),
Type::BorrowedRef { lifetime: _, is_mutable: _, type_ } => self.check_type(&**type_),
Type::QualifiedPath { name: _, args, self_type, trait_ } => {
self.check_generic_args(&**args);
self.check_opt_generic_args(&args);
self.check_type(&**self_type);
if let Some(trait_) = trait_ {
self.check_path(trait_, PathKind::Trait);
Expand Down Expand Up @@ -309,13 +309,12 @@ impl<'a> Validator<'a> {
self.fail(&x.id, ErrorKind::Custom(format!("No entry in '$.paths' for {x:?}")));
}

if let Some(args) = &x.args {
self.check_generic_args(&**args);
}
self.check_opt_generic_args(&x.args);
}

fn check_generic_args(&mut self, x: &'a GenericArgs) {
match x {
fn check_opt_generic_args(&mut self, x: &'a Option<Box<GenericArgs>>) {
let Some(x) = x else { return };
match &**x {
GenericArgs::AngleBracketed { args, constraints } => {
args.iter().for_each(|arg| self.check_generic_arg(arg));
constraints.iter().for_each(|bind| self.check_assoc_item_constraint(bind));
Expand Down Expand Up @@ -355,7 +354,7 @@ impl<'a> Validator<'a> {
}

fn check_assoc_item_constraint(&mut self, bind: &'a AssocItemConstraint) {
self.check_generic_args(&bind.args);
self.check_opt_generic_args(&bind.args);
match &bind.binding {
AssocItemConstraintKind::Equality(term) => self.check_term(term),
AssocItemConstraintKind::Constraint(bounds) => {
Expand Down
20 changes: 20 additions & 0 deletions tests/rustdoc-json/generic-args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
pub struct MyStruct(u32);

pub trait MyTrait {
type MyType;
fn my_fn(&self);
}

impl MyTrait for MyStruct {
type MyType = u32;
fn my_fn(&self) {}
}

//@ is "$.index[?(@.name=='my_fn1')].inner.function.sig.inputs[0][1].qualified_path.args" null
//@ is "$.index[?(@.name=='my_fn1')].inner.function.sig.inputs[0][1].qualified_path.self_type.resolved_path.args" null
pub fn my_fn1(_: <MyStruct as MyTrait>::MyType) {}

//@ is "$.index[?(@.name=='my_fn2')].inner.function.sig.inputs[0][1].dyn_trait.traits[0].trait.args.angle_bracketed.constraints[0].args" null
pub fn my_fn2(_: IntoIterator<Item = MyStruct, IntoIter = impl Clone>) {}

fn main() {}
Loading