diff options
Diffstat (limited to 'src/rustdoc-json-types')
-rw-r--r-- | src/rustdoc-json-types/Cargo.toml | 13 | ||||
-rw-r--r-- | src/rustdoc-json-types/README.md | 12 | ||||
-rw-r--r-- | src/rustdoc-json-types/lib.rs | 609 | ||||
-rw-r--r-- | src/rustdoc-json-types/tests.rs | 34 |
4 files changed, 668 insertions, 0 deletions
diff --git a/src/rustdoc-json-types/Cargo.toml b/src/rustdoc-json-types/Cargo.toml new file mode 100644 index 000000000..d60699efd --- /dev/null +++ b/src/rustdoc-json-types/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "rustdoc-json-types" +version = "0.1.0" +edition = "2021" + +[lib] +path = "lib.rs" + +[dependencies] +serde = { version = "1.0", features = ["derive"] } + +[dev-dependencies] +serde_json = "1.0" diff --git a/src/rustdoc-json-types/README.md b/src/rustdoc-json-types/README.md new file mode 100644 index 000000000..1e67d3765 --- /dev/null +++ b/src/rustdoc-json-types/README.md @@ -0,0 +1,12 @@ +# Rustdoc JSON Types + +This crate exposes the Rustdoc JSON API as a set of types with serde implementations. +These types are part of the public interface of the rustdoc JSON output, and making them +their own crate allows them to be versioned and distributed without having to depend on +any rustc/rustdoc internals. This way, consumers can rely on this crate for both documentation +of the output, and as a way to read the output easily, and its versioning is intended to +follow semver guarantees about the version of the format. JSON format X will always be +compatible with rustdoc-json-types version N. + +Currently, this crate is only used by rustdoc itself. Upon the stabilization of +rustdoc-json, it may be distributed separately for consumers of the API. diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs new file mode 100644 index 000000000..761e94c7e --- /dev/null +++ b/src/rustdoc-json-types/lib.rs @@ -0,0 +1,609 @@ +//! Rustdoc's JSON output interface +//! +//! These types are the public API exposed through the `--output-format json` flag. The [`Crate`] +//! struct is the root of the JSON blob and all other items are contained within. + +use std::collections::HashMap; +use std::path::PathBuf; + +use serde::{Deserialize, Serialize}; + +/// rustdoc format-version. +pub const FORMAT_VERSION: u32 = 16; + +/// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information +/// about the language items in the local crate, as well as info about external items to allow +/// tools to find or link to them. +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Crate { + /// The id of the root [`Module`] item of the local crate. + pub root: Id, + /// The version string given to `--crate-version`, if any. + pub crate_version: Option<String>, + /// Whether or not the output includes private items. + pub includes_private: bool, + /// A collection of all items in the local crate as well as some external traits and their + /// items that are referenced locally. + pub index: HashMap<Id, Item>, + /// Maps IDs to fully qualified paths and other info helpful for generating links. + pub paths: HashMap<Id, ItemSummary>, + /// Maps `crate_id` of items to a crate name and html_root_url if it exists. + pub external_crates: HashMap<u32, ExternalCrate>, + /// A single version number to be used in the future when making backwards incompatible changes + /// to the JSON output. + pub format_version: u32, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct ExternalCrate { + pub name: String, + pub html_root_url: Option<String>, +} + +/// For external (not defined in the local crate) items, you don't get the same level of +/// information. This struct should contain enough to generate a link/reference to the item in +/// question, or can be used by a tool that takes the json output of multiple crates to find +/// the actual item definition with all the relevant info. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct ItemSummary { + /// Can be used to look up the name and html_root_url of the crate this item came from in the + /// `external_crates` map. + pub crate_id: u32, + /// The list of path components for the fully qualified path of this item (e.g. + /// `["std", "io", "lazy", "Lazy"]` for `std::io::lazy::Lazy`). + pub path: Vec<String>, + /// Whether this item is a struct, trait, macro, etc. + pub kind: ItemKind, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Item { + /// The unique identifier of this item. Can be used to find this item in various mappings. + pub id: Id, + /// This can be used as a key to the `external_crates` map of [`Crate`] to see which crate + /// this item came from. + pub crate_id: u32, + /// Some items such as impls don't have names. + pub name: Option<String>, + /// The source location of this item (absent if it came from a macro expansion or inline + /// assembly). + pub span: Option<Span>, + /// By default all documented items are public, but you can tell rustdoc to output private items + /// so this field is needed to differentiate. + pub visibility: Visibility, + /// The full markdown docstring of this item. Absent if there is no documentation at all, + /// Some("") if there is some documentation but it is empty (EG `#[doc = ""]`). + pub docs: Option<String>, + /// This mapping resolves [intra-doc links](https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md) from the docstring to their IDs + pub links: HashMap<String, Id>, + /// Stringified versions of the attributes on this item (e.g. `"#[inline]"`) + pub attrs: Vec<String>, + pub deprecation: Option<Deprecation>, + #[serde(flatten)] + pub inner: ItemEnum, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Span { + /// The path to the source file for this span relative to the path `rustdoc` was invoked with. + pub filename: PathBuf, + /// Zero indexed Line and Column of the first character of the `Span` + pub begin: (usize, usize), + /// Zero indexed Line and Column of the last character of the `Span` + pub end: (usize, usize), +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Deprecation { + pub since: Option<String>, + pub note: Option<String>, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum Visibility { + Public, + /// For the most part items are private by default. The exceptions are associated items of + /// public traits and variants of public enums. + Default, + Crate, + /// For `pub(in path)` visibility. `parent` is the module it's restricted to and `path` is how + /// that module was referenced (like `"super::super"` or `"crate::foo::bar"`). + Restricted { + parent: Id, + path: String, + }, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum GenericArgs { + /// <'a, 32, B: Copy, C = u32> + AngleBracketed { args: Vec<GenericArg>, bindings: Vec<TypeBinding> }, + /// Fn(A, B) -> C + Parenthesized { inputs: Vec<Type>, output: Option<Type> }, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum GenericArg { + Lifetime(String), + Type(Type), + Const(Constant), + Infer, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Constant { + #[serde(rename = "type")] + pub type_: Type, + pub expr: String, + pub value: Option<String>, + pub is_literal: bool, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct TypeBinding { + pub name: String, + pub args: GenericArgs, + pub binding: TypeBindingKind, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum TypeBindingKind { + Equality(Term), + Constraint(Vec<GenericBound>), +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Id(pub String); + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum ItemKind { + Module, + ExternCrate, + Import, + Struct, + StructField, + Union, + Enum, + Variant, + Function, + Typedef, + OpaqueTy, + Constant, + Trait, + TraitAlias, + Method, + Impl, + Static, + ForeignType, + Macro, + ProcAttribute, + ProcDerive, + AssocConst, + AssocType, + Primitive, + Keyword, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(tag = "kind", content = "inner", rename_all = "snake_case")] +pub enum ItemEnum { + Module(Module), + ExternCrate { + name: String, + rename: Option<String>, + }, + Import(Import), + + Union(Union), + Struct(Struct), + StructField(Type), + Enum(Enum), + Variant(Variant), + + Function(Function), + + Trait(Trait), + TraitAlias(TraitAlias), + Method(Method), + Impl(Impl), + + Typedef(Typedef), + OpaqueTy(OpaqueTy), + Constant(Constant), + + Static(Static), + + /// `type`s from an extern block + ForeignType, + + /// Declarative macro_rules! macro + Macro(String), + ProcMacro(ProcMacro), + + PrimitiveType(String), + + AssocConst { + #[serde(rename = "type")] + type_: Type, + /// e.g. `const X: usize = 5;` + default: Option<String>, + }, + AssocType { + generics: Generics, + bounds: Vec<GenericBound>, + /// e.g. `type X = usize;` + default: Option<Type>, + }, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Module { + pub is_crate: bool, + pub items: Vec<Id>, + /// If `true`, this module is not part of the public API, but it contains + /// items that are re-exported as public API. + pub is_stripped: bool, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Union { + pub generics: Generics, + pub fields_stripped: bool, + pub fields: Vec<Id>, + pub impls: Vec<Id>, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Struct { + pub struct_type: StructType, + pub generics: Generics, + pub fields_stripped: bool, + pub fields: Vec<Id>, + pub impls: Vec<Id>, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Enum { + pub generics: Generics, + pub variants_stripped: bool, + pub variants: Vec<Id>, + pub impls: Vec<Id>, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +#[serde(tag = "variant_kind", content = "variant_inner")] +pub enum Variant { + Plain, + Tuple(Vec<Type>), + Struct(Vec<Id>), +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum StructType { + Plain, + Tuple, + Unit, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Header { + #[serde(rename = "const")] + pub const_: bool, + #[serde(rename = "unsafe")] + pub unsafe_: bool, + #[serde(rename = "async")] + pub async_: bool, + pub abi: Abi, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum Abi { + // We only have a concrete listing here for stable ABI's because their are so many + // See rustc_ast_passes::feature_gate::PostExpansionVisitor::check_abi for the list + Rust, + C { unwind: bool }, + Cdecl { unwind: bool }, + Stdcall { unwind: bool }, + Fastcall { unwind: bool }, + Aapcs { unwind: bool }, + Win64 { unwind: bool }, + SysV64 { unwind: bool }, + System { unwind: bool }, + Other(String), +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Function { + pub decl: FnDecl, + pub generics: Generics, + pub header: Header, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Method { + pub decl: FnDecl, + pub generics: Generics, + pub header: Header, + pub has_body: bool, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Generics { + pub params: Vec<GenericParamDef>, + pub where_predicates: Vec<WherePredicate>, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct GenericParamDef { + pub name: String, + pub kind: GenericParamDefKind, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum GenericParamDefKind { + Lifetime { + outlives: Vec<String>, + }, + Type { + bounds: Vec<GenericBound>, + default: Option<Type>, + /// This is normally `false`, which means that this generic parameter is + /// declared in the Rust source text. + /// + /// If it is `true`, this generic parameter has been introduced by the + /// compiler behind the scenes. + /// + /// # Example + /// + /// Consider + /// + /// ```ignore (pseudo-rust) + /// pub fn f(_: impl Trait) {} + /// ``` + /// + /// The compiler will transform this behind the scenes to + /// + /// ```ignore (pseudo-rust) + /// pub fn f<impl Trait: Trait>(_: impl Trait) {} + /// ``` + /// + /// In this example, the generic parameter named `impl Trait` (and which + /// is bound by `Trait`) is synthetic, because it was not originally in + /// the Rust source text. + synthetic: bool, + }, + Const { + #[serde(rename = "type")] + type_: Type, + default: Option<String>, + }, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum WherePredicate { + BoundPredicate { + #[serde(rename = "type")] + type_: Type, + bounds: Vec<GenericBound>, + /// Used for Higher-Rank Trait Bounds (HRTBs) + /// ```plain + /// where for<'a> &'a T: Iterator," + /// ^^^^^^^ + /// | + /// this part + /// ``` + generic_params: Vec<GenericParamDef>, + }, + RegionPredicate { + lifetime: String, + bounds: Vec<GenericBound>, + }, + EqPredicate { + lhs: Type, + rhs: Term, + }, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum GenericBound { + TraitBound { + #[serde(rename = "trait")] + trait_: Type, + /// Used for Higher-Rank Trait Bounds (HRTBs) + /// ```plain + /// where F: for<'a, 'b> Fn(&'a u8, &'b u8) + /// ^^^^^^^^^^^ + /// | + /// this part + /// ``` + generic_params: Vec<GenericParamDef>, + modifier: TraitBoundModifier, + }, + Outlives(String), +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum TraitBoundModifier { + None, + Maybe, + MaybeConst, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum Term { + Type(Type), + Constant(Constant), +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +#[serde(tag = "kind", content = "inner")] +pub enum Type { + /// Structs, enums, and traits + ResolvedPath { + name: String, + id: Id, + args: Option<Box<GenericArgs>>, + param_names: Vec<GenericBound>, + }, + /// Parameterized types + Generic(String), + /// Fixed-size numeric types (plus int/usize/float), char, arrays, slices, and tuples + Primitive(String), + /// `extern "ABI" fn` + FunctionPointer(Box<FunctionPointer>), + /// `(String, u32, Box<usize>)` + Tuple(Vec<Type>), + /// `[u32]` + Slice(Box<Type>), + /// [u32; 15] + Array { + #[serde(rename = "type")] + type_: Box<Type>, + len: String, + }, + /// `impl TraitA + TraitB + ...` + ImplTrait(Vec<GenericBound>), + /// `_` + Infer, + /// `*mut u32`, `*u8`, etc. + RawPointer { + mutable: bool, + #[serde(rename = "type")] + type_: Box<Type>, + }, + /// `&'a mut String`, `&str`, etc. + BorrowedRef { + lifetime: Option<String>, + mutable: bool, + #[serde(rename = "type")] + type_: Box<Type>, + }, + /// `<Type as Trait>::Name` or associated types like `T::Item` where `T: Iterator` + QualifiedPath { + name: String, + args: Box<GenericArgs>, + self_type: Box<Type>, + #[serde(rename = "trait")] + trait_: Box<Type>, + }, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct FunctionPointer { + pub decl: FnDecl, + /// Used for Higher-Rank Trait Bounds (HRTBs) + /// ```plain + /// for<'c> fn(val: &'c i32) -> i32 + /// ^^^^^^^ + /// | + /// this part + /// ``` + pub generic_params: Vec<GenericParamDef>, + pub header: Header, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct FnDecl { + pub inputs: Vec<(String, Type)>, + pub output: Option<Type>, + pub c_variadic: bool, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Trait { + pub is_auto: bool, + pub is_unsafe: bool, + pub items: Vec<Id>, + pub generics: Generics, + pub bounds: Vec<GenericBound>, + pub implementations: Vec<Id>, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct TraitAlias { + pub generics: Generics, + pub params: Vec<GenericBound>, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Impl { + pub is_unsafe: bool, + pub generics: Generics, + pub provided_trait_methods: Vec<String>, + #[serde(rename = "trait")] + pub trait_: Option<Type>, + #[serde(rename = "for")] + pub for_: Type, + pub items: Vec<Id>, + pub negative: bool, + pub synthetic: bool, + pub blanket_impl: Option<Type>, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub struct Import { + /// The full path being imported. + pub source: String, + /// May be different from the last segment of `source` when renaming imports: + /// `use source as name;` + pub name: String, + /// The ID of the item being imported. + pub id: Option<Id>, // FIXME is this actually ever None? + /// Whether this import uses a glob: `use source::*;` + pub glob: bool, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct ProcMacro { + pub kind: MacroKind, + pub helpers: Vec<String>, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum MacroKind { + /// A bang macro `foo!()`. + Bang, + /// An attribute macro `#[foo]`. + Attr, + /// A derive macro `#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]` + Derive, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Typedef { + #[serde(rename = "type")] + pub type_: Type, + pub generics: Generics, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct OpaqueTy { + pub bounds: Vec<GenericBound>, + pub generics: Generics, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Static { + #[serde(rename = "type")] + pub type_: Type, + pub mutable: bool, + pub expr: String, +} + +#[cfg(test)] +mod tests; diff --git a/src/rustdoc-json-types/tests.rs b/src/rustdoc-json-types/tests.rs new file mode 100644 index 000000000..e7f6447ed --- /dev/null +++ b/src/rustdoc-json-types/tests.rs @@ -0,0 +1,34 @@ +use super::*; + +#[test] +fn test_struct_info_roundtrip() { + let s = ItemEnum::Struct(Struct { + struct_type: StructType::Plain, + generics: Generics { params: vec![], where_predicates: vec![] }, + fields_stripped: false, + fields: vec![], + impls: vec![], + }); + + let struct_json = serde_json::to_string(&s).unwrap(); + + let de_s = serde_json::from_str(&struct_json).unwrap(); + + assert_eq!(s, de_s); +} + +#[test] +fn test_union_info_roundtrip() { + let u = ItemEnum::Union(Union { + generics: Generics { params: vec![], where_predicates: vec![] }, + fields_stripped: false, + fields: vec![], + impls: vec![], + }); + + let union_json = serde_json::to_string(&u).unwrap(); + + let de_u = serde_json::from_str(&union_json).unwrap(); + + assert_eq!(u, de_u); +} |