Skip to content

Commit

Permalink
implement notion of 'public_api' for importable paths
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Aug 29, 2023
1 parent 9ac3cd1 commit 45fba69
Show file tree
Hide file tree
Showing 11 changed files with 277 additions and 11 deletions.
12 changes: 10 additions & 2 deletions src/adapter/edges.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use trustfall::provider::{
VertexIterator,
};

use crate::{attributes::Attribute, IndexedCrate};
use crate::{attributes::Attribute, indexed_crate::ImportablePath, IndexedCrate};

use super::{optimizations, origin::Origin, vertex::Vertex, RustdocAdapter};

Expand Down Expand Up @@ -79,7 +79,15 @@ pub(super) fn resolve_importable_edge<'a>(
parent_crate
.publicly_importable_names(item_id)
.into_iter()
.map(move |x| origin.make_importable_path_vertex(x)),
.map(move |x| {
// TODO: fold doc_hidden and deprecated along the whole path
origin.make_importable_path_vertex(ImportablePath::new(
x,
// TODO: parse the attribute properly to allow for other args to doc
item.attrs.iter().any(|attr| attr == "#[doc(hidden)]"),
item.deprecation.is_some(),
))
}),
)
}),
_ => unreachable!("resolve_importable_edge {edge_name}"),
Expand Down
7 changes: 5 additions & 2 deletions src/adapter/origin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ use std::rc::Rc;

use rustdoc_types::{Abi, Item, Span};

use crate::attributes::{Attribute, AttributeMetaItem};
use crate::{
attributes::{Attribute, AttributeMetaItem},
indexed_crate::ImportablePath,
};

use super::vertex::{Vertex, VertexKind};

Expand Down Expand Up @@ -37,7 +40,7 @@ impl Origin {

pub(super) fn make_importable_path_vertex<'a>(
&self,
importable_path: Vec<&'a str>,
importable_path: ImportablePath<'a>,
) -> Vertex<'a> {
Vertex {
origin: *self,
Expand Down
10 changes: 10 additions & 0 deletions src/adapter/properties.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,12 +152,22 @@ pub(super) fn resolve_importable_path_property<'a>(
vertex
.as_importable_path()
.expect("not an importable path")
.components
.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.into()
}),
"visibility_limit" => resolve_property_with(contexts, |_| "public".into()),
"doc_hidden" => {
resolve_property_with(contexts, field_property!(as_importable_path, doc_hidden))
}
"deprecated" => {
resolve_property_with(contexts, field_property!(as_importable_path, deprecated))
}
"public_api" => {
resolve_property_with(contexts, accessor_property!(as_importable_path, public_api))
}
_ => unreachable!("ImportablePath property {property_name}"),
}
}
Expand Down
155 changes: 155 additions & 0 deletions src/adapter/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -655,3 +655,158 @@ fn function_export_name() {
results
);
}

#[test]
fn importable_paths() {
let path = "./localdata/test_data/importable_paths/rustdoc.json";
let content = std::fs::read_to_string(path)
.with_context(|| format!("Could not load {path} file, did you forget to run ./scripts/regenerate_test_rustdocs.sh ?"))
.expect("failed to load rustdoc");

let crate_ = serde_json::from_str(&content).expect("failed to parse rustdoc");
let indexed_crate = IndexedCrate::new(&crate_);
let adapter = Arc::new(RustdocAdapter::new(&indexed_crate, None));

let query = r#"
{
Crate {
item {
... on Struct {
name @output
importable_path @fold {
path @output
doc_hidden @output
deprecated @output
public_api @output
}
}
}
}
}
"#;

let variables: BTreeMap<&str, &str> = BTreeMap::default();

let schema =
Schema::parse(include_str!("../rustdoc_schema.graphql")).expect("schema failed to parse");

#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, serde::Deserialize)]
struct Output {
name: String,
path: Vec<Vec<String>>,
doc_hidden: Vec<bool>,
deprecated: Vec<bool>,
public_api: Vec<bool>,
}

let mut results: Vec<_> =
trustfall::execute_query(&schema, adapter.clone(), query, variables.clone())
.expect("failed to run query")
.map(|row| row.try_into_struct().expect("shape mismatch"))
.collect();
results.sort_unstable();

similar_asserts::assert_eq!(
vec![
Output {
name: "DeprecatedHidden".into(),
path: vec![vec![
"importable_paths".into(),
"submodule".into(),
"DeprecatedHidden".into(),
],],
doc_hidden: vec![true,],
deprecated: vec![true,],
public_api: vec![true,],
},
Output {
name: "DeprecatedModuleHidden".into(),
path: vec![vec![
"importable_paths".into(),
"hidden".into(),
"DeprecatedModuleHidden".into(),
],],
doc_hidden: vec![false,],
deprecated: vec![true,],
public_api: vec![true,],
},
Output {
name: "Hidden".into(),
path: vec![vec![
"importable_paths".into(),
"submodule".into(),
"Hidden".into(),
],],
doc_hidden: vec![true,],
deprecated: vec![false,],
public_api: vec![false,],
},
Output {
name: "ModuleDeprecated".into(),
path: vec![
vec![
"importable_paths".into(),
"deprecated".into(),
"ModuleDeprecated".into(),
],
vec!["importable_paths".into(), "UsedModuleDeprecated".into(),],
],
doc_hidden: vec![false, false],
deprecated: vec![true, true],
public_api: vec![true, true],
},
Output {
name: "ModuleDeprecatedHidden".into(),
path: vec![vec![
"importable_paths".into(),
"deprecated".into(),
"ModuleDeprecatedHidden".into(),
],],
doc_hidden: vec![true,],
deprecated: vec![true,],
public_api: vec![true,],
},
Output {
name: "ModuleDeprecatedModuleHidden".into(),
path: vec![vec![
"importable_paths".into(),
"hidden".into(),
"deprecated".into(),
"ModuleDeprecatedModuleHidden".into(),
],],
doc_hidden: vec![false,],
deprecated: vec![true,],
public_api: vec![true,],
},
Output {
name: "ModuleHidden".into(),
path: vec![
vec!["importable_paths".into(), "UsedVisible".into(),],
vec![
"importable_paths".into(),
"hidden".into(),
"ModuleHidden".into(),
],
],
doc_hidden: vec![false, false,],
deprecated: vec![false, false,],
public_api: vec![true, true,],
},
Output {
name: "Private".into(),
path: vec![],
doc_hidden: vec![],
deprecated: vec![],
public_api: vec![],
},
Output {
name: "PublicImportable".into(),
path: vec![vec!["importable_paths".into(), "PublicImportable".into(),],],
doc_hidden: vec![false,],
deprecated: vec![false,],
public_api: vec![true,],
}
],
results
);
}
5 changes: 3 additions & 2 deletions src/adapter/vertex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use trustfall::provider::Typename;

use crate::{
attributes::{Attribute, AttributeMetaItem},
indexed_crate::ImportablePath,
IndexedCrate,
};

Expand All @@ -28,7 +29,7 @@ pub enum VertexKind<'a> {
Item(&'a Item),
Span(&'a Span),
Path(&'a [String]),
ImportablePath(Vec<&'a str>),
ImportablePath(ImportablePath<'a>),
RawType(&'a Type),
Attribute(Attribute<'a>),
AttributeMetaItem(Rc<AttributeMetaItem<'a>>),
Expand Down Expand Up @@ -161,7 +162,7 @@ impl<'a> Vertex<'a> {
}
}

pub(super) fn as_importable_path(&self) -> Option<&'_ Vec<&'a str>> {
pub(super) fn as_importable_path(&self) -> Option<&'_ ImportablePath<'a>> {
match &self.kind {
VertexKind::ImportablePath(path) => Some(path),
_ => None,
Expand Down
Empty file added src/importable_path.rs
Empty file.
20 changes: 16 additions & 4 deletions src/indexed_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,9 @@ impl<'a> IndexedCrate<'a> {
.filter(|item| supported_item_kind(item))
{
for importable_path in value.publicly_importable_names(&item.id) {
// TODO: doc_hidden and deprecated are not relevant in this index
imports_index
.entry(ImportablePath::new(importable_path))
.entry(ImportablePath::new(importable_path, false, false))
.or_default()
.push(item);
}
Expand Down Expand Up @@ -162,13 +163,24 @@ impl<'a> IndexedCrate<'a> {
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct ImportablePath<'a> {
#[non_exhaustive]
pub struct ImportablePath<'a> {
pub(crate) components: Vec<&'a str>,
pub(crate) doc_hidden: bool,
pub(crate) deprecated: bool,
}

impl<'a> ImportablePath<'a> {
fn new(components: Vec<&'a str>) -> Self {
Self { components }
pub(crate) fn new(components: Vec<&'a str>, doc_hidden: bool, deprecated: bool) -> Self {
Self {
components,
doc_hidden,
deprecated,
}
}

pub(crate) fn public_api(&self) -> bool {
self.deprecated || !self.doc_hidden
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,7 @@ mod visibility_tracker;
// Re-export the Crate type so we can deserialize it.
pub use rustdoc_types::Crate;

pub use {adapter::RustdocAdapter, indexed_crate::IndexedCrate};
pub use {
adapter::RustdocAdapter,
indexed_crate::{ImportablePath, IndexedCrate},
};
18 changes: 18 additions & 0 deletions src/rustdoc_schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,24 @@ type ImportablePath {
"""
visibility_limit: String!

"""
This path is hidden in documentation.
"""
doc_hidden: Boolean!

"""
This path is deprecated.
"""
deprecated: Boolean!

"""
This path should be treated as public API. This is true if:
- the path is visible in documentation, or
- the path is not visible and is also deprecated.
(Which assumes that in the past it was a public API.)
"""
public_api: Boolean!

"""
The path from which the item can be imported.
Expand Down
9 changes: 9 additions & 0 deletions test_crates/importable_paths/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "importable_paths"
version = "0.1.0"
edition = "2021"
publish = false

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
47 changes: 47 additions & 0 deletions test_crates/importable_paths/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
pub struct PublicImportable {}

mod private {
pub struct Private {}
}

#[doc(hidden)]
pub mod hidden {
pub struct ModuleHidden {}

#[deprecated]
pub struct DeprecatedModuleHidden {} // public_api

#[deprecated]
pub mod deprecated {
pub struct ModuleDeprecatedModuleHidden {} // public_api
}
}

pub mod submodule {
#[doc(hidden)]
pub struct Hidden {}

#[deprecated]
#[doc(hidden)]
pub struct DeprecatedHidden {} // public_api
}

#[deprecated]
pub mod deprecated {
pub struct ModuleDeprecated {} // public_api

#[doc(hidden)]
pub struct ModuleDeprecatedHidden {} // public_api
}

// This is expected to be visible in rustdoc.
pub use hidden::ModuleHidden as UsedVisible; // public_api

// This is expected to be hidden in rustdoc.
pub use submodule::Hidden as UsedHidden;

// This is expected to be public_api and deprecated
pub use deprecated::ModuleDeprecated as UsedModuleDeprecated;

// not public_api; it's not relevant that the module was deprecated
pub use deprecated::ModuleDeprecatedHidden as UsedModuleDeprecatedHidden;

0 comments on commit 45fba69

Please sign in to comment.