Skip to content

Commit

Permalink
Support querying proc macros of all three kinds. (#486) (#489)
Browse files Browse the repository at this point in the history
  • Loading branch information
obi1kenobi authored Sep 26, 2024
1 parent 9c9e172 commit 501b66f
Show file tree
Hide file tree
Showing 9 changed files with 612 additions and 9 deletions.
22 changes: 22 additions & 0 deletions src/adapter/edges.rs
Original file line number Diff line number Diff line change
Expand Up @@ -723,3 +723,25 @@ pub(super) fn resolve_attribute_meta_item_edge<'a, V: AsVertex<Vertex<'a>> + 'a>
_ => unreachable!("resolve_attribute_meta_item_edge {edge_name}"),
}
}

pub(super) fn resolve_derive_proc_macro_edge<'a, V: AsVertex<Vertex<'a>> + 'a>(
contexts: ContextIterator<'a, V>,
edge_name: &str,
) -> ContextOutcomeIterator<'a, V, VertexIterator<'a, Vertex<'a>>> {
match edge_name {
"helper_attribute" => resolve_neighbors_with(contexts, move |vertex| {
let origin = vertex.origin;

let proc_macro = vertex
.as_proc_macro()
.expect("vertex was not a DeriveProcMacro");
Box::new(
proc_macro
.helpers
.iter()
.map(move |helper| origin.make_derive_helper_attr_vertex(helper)),
)
}),
_ => unreachable!("resolve_derive_proc_macro_edge {edge_name}"),
}
}
71 changes: 62 additions & 9 deletions src/adapter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,30 @@ impl<'a> Adapter<'a> for RustdocAdapter<'a> {
match type_name.as_ref() {
"Crate" => properties::resolve_crate_property(contexts, property_name),
"Item" => properties::resolve_item_property(contexts, property_name),
"ImplOwner" | "Struct" | "StructField" | "Enum" | "Variant" | "PlainVariant"
| "TupleVariant" | "StructVariant" | "Union" | "Trait" | "Function" | "Method"
| "Impl" | "GlobalValue" | "Constant" | "Static" | "AssociatedType"
| "AssociatedConstant" | "Module" | "Macro"
"ImplOwner"
| "Struct"
| "StructField"
| "Enum"
| "Variant"
| "PlainVariant"
| "TupleVariant"
| "StructVariant"
| "Union"
| "Trait"
| "Function"
| "Method"
| "Impl"
| "GlobalValue"
| "Constant"
| "Static"
| "AssociatedType"
| "AssociatedConstant"
| "Module"
| "Macro"
| "ProcMacro"
| "FunctionLikeProcMacro"
| "AttributeProcMacro"
| "DeriveProcMacro"
if matches!(
property_name.as_ref(),
"id" | "crate_id"
Expand Down Expand Up @@ -162,6 +182,12 @@ impl<'a> Adapter<'a> for RustdocAdapter<'a> {
"Discriminant" => {
properties::resolve_discriminant_property(contexts, property_name)
}
"DeriveMacroHelperAttribute" => {
properties::resolve_derive_macro_helper_attribute_property(
contexts,
property_name,
)
}
_ => unreachable!("resolve_property {type_name} {property_name}"),
}
}
Expand Down Expand Up @@ -189,10 +215,31 @@ impl<'a> Adapter<'a> for RustdocAdapter<'a> {
self.previous_crate,
)
}
"Item" | "ImplOwner" | "Struct" | "StructField" | "Enum" | "Variant"
| "PlainVariant" | "TupleVariant" | "Union" | "StructVariant" | "Trait"
| "Function" | "Method" | "Impl" | "GlobalValue" | "Constant" | "Static"
| "AssociatedType" | "AssociatedConstant" | "Module" | "Macro"
"Item"
| "ImplOwner"
| "Struct"
| "StructField"
| "Enum"
| "Variant"
| "PlainVariant"
| "TupleVariant"
| "Union"
| "StructVariant"
| "Trait"
| "Function"
| "Method"
| "Impl"
| "GlobalValue"
| "Constant"
| "Static"
| "AssociatedType"
| "AssociatedConstant"
| "Module"
| "Macro"
| "ProcMacro"
| "FunctionLikeProcMacro"
| "AttributeProcMacro"
| "DeriveProcMacro"
if matches!(edge_name.as_ref(), "span" | "attribute") =>
{
edges::resolve_item_edge(contexts, edge_name)
Expand Down Expand Up @@ -250,6 +297,7 @@ impl<'a> Adapter<'a> for RustdocAdapter<'a> {
"ImplementedTrait" => edges::resolve_implemented_trait_edge(contexts, edge_name),
"Attribute" => edges::resolve_attribute_edge(contexts, edge_name),
"AttributeMetaItem" => edges::resolve_attribute_meta_item_edge(contexts, edge_name),
"DeriveProcMacro" => edges::resolve_derive_proc_macro_edge(contexts, edge_name),
_ => unreachable!("resolve_neighbors {type_name} {edge_name} {parameters:?}"),
}
}
Expand All @@ -264,7 +312,7 @@ impl<'a> Adapter<'a> for RustdocAdapter<'a> {
let coerce_to_type = coerce_to_type.clone();
match type_name.as_ref() {
"Item" | "Variant" | "FunctionLike" | "Importable" | "ImplOwner" | "RawType"
| "GlobalValue" => {
| "GlobalValue" | "ProcMacro" => {
resolve_coercion_with(contexts, move |vertex| {
let actual_type_name = vertex.typename();

Expand All @@ -275,6 +323,10 @@ impl<'a> Adapter<'a> for RustdocAdapter<'a> {
),
"ImplOwner" => matches!(actual_type_name, "Struct" | "Enum" | "Union"),
"GlobalValue" => matches!(actual_type_name, "Constant" | "Static",),
"ProcMacro" => matches!(
actual_type_name,
"FunctionLikeProcMacro" | "AttributeProcMacro" | "DeriveProcMacro"
),
_ => {
// The remaining types are final (don't have any subtypes)
// so we can just compare the actual type name to
Expand Down Expand Up @@ -305,5 +357,6 @@ pub(crate) fn supported_item_kind(item: &Item) -> bool {
| rustdoc_types::ItemEnum::AssocType { .. }
| rustdoc_types::ItemEnum::Module { .. }
| rustdoc_types::ItemEnum::Macro { .. }
| rustdoc_types::ItemEnum::ProcMacro { .. }
)
}
7 changes: 7 additions & 0 deletions src/adapter/origin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,4 +118,11 @@ impl Origin {
kind: VertexKind::Variant(EnumVariant::new(item, discriminants, index)),
}
}

pub(super) fn make_derive_helper_attr_vertex<'a>(&self, helper: &'a str) -> Vertex<'a> {
Vertex {
origin: *self,
kind: VertexKind::DeriveHelperAttr(helper),
}
}
}
16 changes: 16 additions & 0 deletions src/adapter/properties.rs
Original file line number Diff line number Diff line change
Expand Up @@ -594,3 +594,19 @@ pub(crate) fn resolve_discriminant_property<'a, V: AsVertex<Vertex<'a>> + 'a>(
_ => unreachable!("Discriminant property {property_name}"),
}
}

pub(crate) fn resolve_derive_macro_helper_attribute_property<'a, V: AsVertex<Vertex<'a>> + 'a>(
contexts: ContextIterator<'a, V>,
property_name: &str,
) -> ContextOutcomeIterator<'a, V, FieldValue> {
match property_name {
"name" => resolve_property_with(contexts, |vertex| {
vertex
.as_derive_helper_attr()
.expect("vertex was not a DeriveMacroHelperAttribute")
.to_string()
.into()
}),
_ => unreachable!("DeriveMacroHelperAttribute property {property_name}"),
}
}
135 changes: 135 additions & 0 deletions src/adapter/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2277,3 +2277,138 @@ fn declarative_macros() {

similar_asserts::assert_eq!(expected_results, results);
}

#[test]
fn proc_macros() {
let path = "./localdata/test_data/proc_macros/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 ProcMacro {
kind: __typename @output
name @output
public_api_eligible @output
visibility_limit @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 {
kind: String,
name: String,
public_api_eligible: bool,
visibility_limit: String,
}

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();

// We write the results in the order the items appear in the test file,
// and sort them afterward in order to compare with the (sorted) query results.
// This makes it easier to verify that the expected data here is correct
// by reading it side-by-side with the file.
let mut expected_results = vec![
Output {
kind: "FunctionLikeProcMacro".into(),
name: "make_answer".into(),
public_api_eligible: true,
visibility_limit: "public".into(),
},
Output {
kind: "AttributeProcMacro".into(),
name: "return_as_is".into(),
public_api_eligible: true,
visibility_limit: "public".into(),
},
Output {
kind: "DeriveProcMacro".into(),
name: "AnswerFn".into(),
public_api_eligible: true,
visibility_limit: "public".into(),
},
Output {
kind: "DeriveProcMacro".into(),
name: "HelperAttr".into(),
public_api_eligible: true,
visibility_limit: "public".into(),
},
Output {
kind: "FunctionLikeProcMacro".into(),
name: "hidden".into(),
public_api_eligible: false,
visibility_limit: "public".into(),
},
];
expected_results.sort_unstable();

similar_asserts::assert_eq!(expected_results, results);

// Ensure that derive macro helper attributes can be queried correctly.
let query = r#"
{
Crate {
item {
... on DeriveProcMacro {
name @output
helper_attribute {
attr: name @output
}
}
}
}
}
"#;

#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, serde::Deserialize)]
struct DeriveOutput {
name: String,
attr: String,
}

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();

// We write the results in the order the items appear in the test file,
// and sort them afterward in order to compare with the (sorted) query results.
// This makes it easier to verify that the expected data here is correct
// by reading it side-by-side with the file.
let mut expected_results = vec![
DeriveOutput {
name: "HelperAttr".into(),
attr: "helper".into(),
},
DeriveOutput {
name: "HelperAttr".into(),
attr: "second".into(),
},
];
expected_results.sort_unstable();

similar_asserts::assert_eq!(expected_results, results);
}
21 changes: 21 additions & 0 deletions src/adapter/vertex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub enum VertexKind<'a> {
FunctionAbi(&'a Abi),
Discriminant(Cow<'a, str>),
Variant(EnumVariant<'a>),
DeriveHelperAttr(&'a str),
}

impl<'a> Typename for Vertex<'a> {
Expand All @@ -64,6 +65,11 @@ impl<'a> Typename for Vertex<'a> {
rustdoc_types::ItemEnum::Static(..) => "Static",
rustdoc_types::ItemEnum::AssocType { .. } => "AssociatedType",
rustdoc_types::ItemEnum::Macro { .. } => "Macro",
rustdoc_types::ItemEnum::ProcMacro(proc) => match proc.kind {
rustdoc_types::MacroKind::Bang => "FunctionLikeProcMacro",
rustdoc_types::MacroKind::Attr => "AttributeProcMacro",
rustdoc_types::MacroKind::Derive => "DeriveProcMacro",
},
_ => unreachable!("unexpected item.inner for item: {item:?}"),
},
VertexKind::Span(..) => "Span",
Expand All @@ -86,6 +92,7 @@ impl<'a> Typename for Vertex<'a> {
VariantKind::Tuple(..) => "TupleVariant",
VariantKind::Struct { .. } => "StructVariant",
},
VertexKind::DeriveHelperAttr(..) => "DeriveMacroHelperAttribute",
}
}
}
Expand Down Expand Up @@ -237,6 +244,13 @@ impl<'a> Vertex<'a> {
})
}

pub(super) fn as_proc_macro(&self) -> Option<&'a rustdoc_types::ProcMacro> {
self.as_item().and_then(|item| match &item.inner {
rustdoc_types::ItemEnum::ProcMacro(m) => Some(m),
_ => None,
})
}

pub(super) fn as_attribute(&self) -> Option<&'_ Attribute<'a>> {
match &self.kind {
VertexKind::Attribute(attr) => Some(attr),
Expand Down Expand Up @@ -271,6 +285,13 @@ impl<'a> Vertex<'a> {
_ => None,
}
}

pub(super) fn as_derive_helper_attr(&self) -> Option<&'a str> {
match &self.kind {
VertexKind::DeriveHelperAttr(value) => Some(value),
_ => None,
}
}
}

impl<'a> From<&'a Item> for VertexKind<'a> {
Expand Down
Loading

0 comments on commit 501b66f

Please sign in to comment.